{-# LANGUAGE Trustworthy #-}
module System.IO.Streams.Brotli
(
compress
, decompress
, compressWith
, Brotli.defaultCompressParams
, Brotli.CompressParams
, Brotli.compressLevel
, Brotli.compressWindowSize
, Brotli.compressMode
, Brotli.compressSizeHint
, Brotli.CompressionLevel(..)
, Brotli.CompressionWindowSize(..)
, Brotli.CompressionMode(..)
, decompressWith
, Brotli.defaultDecompressParams
, Brotli.DecompressParams
, Brotli.decompressDisableRingBufferReallocation
) where
import Codec.Compression.Brotli (DecompressStream(..), CompressStream(..))
import qualified Codec.Compression.Brotli as Brotli
import Control.Exception
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.IORef
import Data.Maybe
import System.IO.Streams (InputStream, OutputStream,
makeInputStream, makeOutputStream)
import qualified System.IO.Streams as Streams
decompress :: InputStream ByteString -> IO (InputStream ByteString)
decompress = decompressWith Brotli.defaultDecompressParams
decompressWith :: Brotli.DecompressParams -> InputStream ByteString -> IO (InputStream ByteString)
decompressWith parms ibs = do
st <- newIORef =<< Brotli.decompressIO parms
makeInputStream (go st)
where
go stref = do
st' <- goFeed =<< readIORef stref
case st' of
DecompressInputRequired _ -> do
writeIORef stref st'
fail "the impossible happened"
DecompressOutputAvailable outb next -> do
writeIORef stref =<< next
return (Just outb)
DecompressStreamEnd leftover -> do
unless (BS.null leftover) $ do
Streams.unRead leftover ibs
writeIORef stref (DecompressStreamEnd BS.empty)
return Nothing
DecompressStreamError rc -> do
writeIORef stref st'
throwIO rc
goFeed (DecompressInputRequired supply) =
goFeed =<< supply . fromMaybe BS.empty =<< getChunk
goFeed s = return s
getChunk = do
mbs <- Streams.read ibs
case mbs of
Just bs | BS.null bs -> getChunk
_ -> return mbs
compress :: OutputStream ByteString -> IO (OutputStream ByteString)
compress = compressWith Brotli.defaultCompressParams
compressWith :: Brotli.CompressParams -> OutputStream ByteString -> IO (OutputStream ByteString)
compressWith parms obs = do
st <- newIORef =<< Brotli.compressIO parms
makeOutputStream (go st)
where
go stref (Just chunk) = do
st <- readIORef stref
st' <- case st of
CompressInputRequired flush supply
| BS.null chunk -> goOutput True =<< flush
| otherwise -> goOutput False =<< supply chunk
_ -> fail "compressWith: unexpected state"
writeIORef stref st'
case st' of
CompressInputRequired _ _ -> return ()
_ -> fail "compressWith: unexpected state"
go stref Nothing = do
st <- readIORef stref
st' <- case st of
CompressInputRequired _ supply -> goOutput False =<< supply BS.empty
_ -> fail "compressWith[EOF]: unexpected state"
writeIORef stref st'
case st' of
CompressStreamEnd -> return ()
_ -> fail "compressWith[EOF]: unexpected state"
goOutput flush st@(CompressInputRequired _ _) = do
when flush $
Streams.write (Just BS.empty) obs
return st
goOutput flush (CompressOutputAvailable obuf next) = do
Streams.write (Just obuf) obs
goOutput flush =<< next
goOutput _ st@CompressStreamEnd = do
Streams.write Nothing obs
return st