{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Avro.Codec (
Codec(..)
, Decompress
, nullCodec
, deflateCodec
) where
import Codec.Compression.Zlib.Internal as Zlib
import qualified Data.Binary.Get as G
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
type Decompress a = LBS.ByteString -> G.Get a -> Either String a
data Codec = Codec
{
codecName :: ByteString
, codecDecompress :: forall a. Decompress a
, codecCompress :: LBS.ByteString -> LBS.ByteString
}
nullCodec :: Codec
nullCodec =
Codec
{
codecName = "null"
, codecDecompress = \input parser ->
case G.runGetOrFail parser input of
Right (_, _, x) -> Right x
Left (_, _, err) -> Left err
, codecCompress = id
}
deflateCodec :: Codec
deflateCodec =
Codec
{
codecName = "deflate"
, codecDecompress = deflateDecompress
, codecCompress = deflateCompress
}
deflateCompress :: LBS.ByteString -> LBS.ByteString
deflateCompress =
Zlib.compress Zlib.rawFormat Zlib.defaultCompressParams
data Chunk
= ChunkRest LBS.ByteString
| ChunkBytes ByteString
| ChunkError Zlib.DecompressError
deflateDecompress :: forall a. LBS.ByteString -> G.Get a -> Either String a
deflateDecompress bytes parser = do
let
chunks :: [Chunk]
chunks =
Zlib.foldDecompressStreamWithInput
(\x xs -> ChunkBytes x : xs)
(\rest -> [ChunkRest rest])
(\err -> [ChunkError err])
(Zlib.decompressST Zlib.rawFormat Zlib.defaultDecompressParams)
bytes
decode :: G.Decoder a -> [Chunk] -> Either String (G.Decoder a)
decode !dec@G.Fail{} _ =
pure dec
decode !dec [] =
pure dec
decode !dec (inchunk : inchunks) =
case inchunk of
ChunkBytes x ->
decode (G.pushChunk dec x) inchunks
ChunkError err ->
Left (show err)
ChunkRest rest -> do
let
dec' = G.pushEndOfInput dec
pure $ G.pushChunks dec' rest
dec <- decode (G.runGetIncremental parser) chunks
case dec of
G.Fail _ _ err ->
Left err
G.Partial{} ->
Left "deflate: Not enough input"
G.Done _ _ x ->
Right x