-- | Intended for unqualified import:
--
-- > import module Codec.Compression.SnappyC.Internal.FrameFormat

module Codec.Compression.SnappyC.Internal.FrameFormat
  ( -- * The 'Frame' type
    Frame(..)
  , FrameIdentifier(..)

    -- ** Encoding
  , Encoder(..)
  , EncodeParams(..)
  , FrameSize -- Opaque
  , Threshold(..)
  , EncodeState(..)
  , EncodeResult(..)
  , initializeEncoder
  , defaultEncodeParams
  , finalizeEncoder
  , encodeBuffered
  , customFrameSize
  , unFrameSize
  , defaultFrameSize

    -- ** Decoding
  , Decoder(..)
  , DecodeParams(..)
  , DecodeState(..)
  , DecodeResult(..)
  , DecodeFailure(..)
  , initializeDecoder
  , defaultDecodeParams
  , finalizeDecoder
  , decodeBuffered
  ) where

import Codec.Compression.SnappyC.Internal.Buffer (Buffer)
import Codec.Compression.SnappyC.Internal.Buffer qualified as Buffer
import Codec.Compression.SnappyC.Internal.Checksum (Checksum)
import Codec.Compression.SnappyC.Internal.Checksum qualified as Checksum
import Codec.Compression.SnappyC.Raw qualified as Raw

import Control.Exception
import Control.Monad.Error.Class
import Data.Bits
import Data.ByteString qualified as Strict (ByteString)
import Data.ByteString qualified as BS.Strict
import Data.Default
import Data.Word
import Text.Printf
import Control.Monad

-- | Snappy frames consist of a header and a payload.
data Frame =
      Frame
        { Frame -> FrameHeader
frameHeader :: !FrameHeader
        , Frame -> ByteString
framePayload :: !Strict.ByteString
        }
  deriving Int -> Frame -> ShowS
[Frame] -> ShowS
Frame -> [Char]
(Int -> Frame -> ShowS)
-> (Frame -> [Char]) -> ([Frame] -> ShowS) -> Show Frame
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Frame -> ShowS
showsPrec :: Int -> Frame -> ShowS
$cshow :: Frame -> [Char]
show :: Frame -> [Char]
$cshowList :: [Frame] -> ShowS
showList :: [Frame] -> ShowS
Show

-- | A frame's header contains an identifier corresponding to the frame type and
-- the size of the payload.
data FrameHeader =
      FrameHeader
        { FrameHeader -> FrameIdentifier
frameHeaderIdentifier :: !FrameIdentifier
        , FrameHeader -> Int
frameHeaderPayloadSize :: !Int
        }
  deriving (Int -> FrameHeader -> ShowS
[FrameHeader] -> ShowS
FrameHeader -> [Char]
(Int -> FrameHeader -> ShowS)
-> (FrameHeader -> [Char])
-> ([FrameHeader] -> ShowS)
-> Show FrameHeader
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> FrameHeader -> ShowS
showsPrec :: Int -> FrameHeader -> ShowS
$cshow :: FrameHeader -> [Char]
show :: FrameHeader -> [Char]
$cshowList :: [FrameHeader] -> ShowS
showList :: [FrameHeader] -> ShowS
Show, FrameHeader -> FrameHeader -> Bool
(FrameHeader -> FrameHeader -> Bool)
-> (FrameHeader -> FrameHeader -> Bool) -> Eq FrameHeader
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: FrameHeader -> FrameHeader -> Bool
== :: FrameHeader -> FrameHeader -> Bool
$c/= :: FrameHeader -> FrameHeader -> Bool
/= :: FrameHeader -> FrameHeader -> Bool
Eq)

-- | Snappy frame identifiers.
data FrameIdentifier =
      StreamId
    | Compressed
    | Uncompressed
    | Padding
    | ReservedUnskippable Word8
    | ReservedSkippable Word8
  deriving (Int -> FrameIdentifier -> ShowS
[FrameIdentifier] -> ShowS
FrameIdentifier -> [Char]
(Int -> FrameIdentifier -> ShowS)
-> (FrameIdentifier -> [Char])
-> ([FrameIdentifier] -> ShowS)
-> Show FrameIdentifier
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> FrameIdentifier -> ShowS
showsPrec :: Int -> FrameIdentifier -> ShowS
$cshow :: FrameIdentifier -> [Char]
show :: FrameIdentifier -> [Char]
$cshowList :: [FrameIdentifier] -> ShowS
showList :: [FrameIdentifier] -> ShowS
Show, FrameIdentifier -> FrameIdentifier -> Bool
(FrameIdentifier -> FrameIdentifier -> Bool)
-> (FrameIdentifier -> FrameIdentifier -> Bool)
-> Eq FrameIdentifier
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: FrameIdentifier -> FrameIdentifier -> Bool
== :: FrameIdentifier -> FrameIdentifier -> Bool
$c/= :: FrameIdentifier -> FrameIdentifier -> Bool
/= :: FrameIdentifier -> FrameIdentifier -> Bool
Eq)

-- | The one byte value corresponding to an identifier.
encodeFrameIdentifier :: FrameIdentifier -> Word8
encodeFrameIdentifier :: FrameIdentifier -> Word8
encodeFrameIdentifier FrameIdentifier
StreamId                  = Word8
0xff
encodeFrameIdentifier FrameIdentifier
Compressed                = Word8
0x00
encodeFrameIdentifier FrameIdentifier
Uncompressed              = Word8
0x01
encodeFrameIdentifier FrameIdentifier
Padding                   = Word8
0xfd
encodeFrameIdentifier (ReservedUnskippable Word8
fid) = Word8
fid
encodeFrameIdentifier (ReservedSkippable Word8
fid)   = Word8
fid

-- | Encode an 'Int' as a three byte little-endian value.
encodeFrameHeader :: FrameHeader -> Strict.ByteString
encodeFrameHeader :: FrameHeader -> ByteString
encodeFrameHeader (FrameHeader FrameIdentifier
ident Int
size)=
    [Word8] -> ByteString
BS.Strict.pack
      [ FrameIdentifier -> Word8
encodeFrameIdentifier FrameIdentifier
ident
      , Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral   (Word32
w32Size Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0x000000ff)
      , Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32 -> Word8) -> Word32 -> Word8
forall a b. (a -> b) -> a -> b
$ (Word32
w32Size Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0x0000ff00) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
8
      , Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32 -> Word8) -> Word32 -> Word8
forall a b. (a -> b) -> a -> b
$ (Word32
w32Size Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0x00ff0000) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
16
      ]
  where
    w32Size :: Word32
    w32Size :: Word32
w32Size = Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size

-------------------------------------------------------------------------------
-- Encoding Snappy frames
-------------------------------------------------------------------------------

-- | Buffers uncompressed data for compression.
newtype Encoder = Encoder { Encoder -> Buffer
encoderBuffer :: Buffer }
  deriving Int -> Encoder -> ShowS
[Encoder] -> ShowS
Encoder -> [Char]
(Int -> Encoder -> ShowS)
-> (Encoder -> [Char]) -> ([Encoder] -> ShowS) -> Show Encoder
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Encoder -> ShowS
showsPrec :: Int -> Encoder -> ShowS
$cshow :: Encoder -> [Char]
show :: Encoder -> [Char]
$cshowList :: [Encoder] -> ShowS
showList :: [Encoder] -> ShowS
Show

-- | Determines how much data is put in each Snappy frame and whether it is
-- compressed.
data EncodeParams =
      EncodeParams
        { -- | Exact amount of uncompressed data included in a single frame.
          EncodeParams -> FrameSize
frameSize :: !FrameSize

          -- | Compression threshold.
        , EncodeParams -> Threshold
threshold :: !Threshold
        }
  deriving (Int -> EncodeParams -> ShowS
[EncodeParams] -> ShowS
EncodeParams -> [Char]
(Int -> EncodeParams -> ShowS)
-> (EncodeParams -> [Char])
-> ([EncodeParams] -> ShowS)
-> Show EncodeParams
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> EncodeParams -> ShowS
showsPrec :: Int -> EncodeParams -> ShowS
$cshow :: EncodeParams -> [Char]
show :: EncodeParams -> [Char]
$cshowList :: [EncodeParams] -> ShowS
showList :: [EncodeParams] -> ShowS
Show, EncodeParams -> EncodeParams -> Bool
(EncodeParams -> EncodeParams -> Bool)
-> (EncodeParams -> EncodeParams -> Bool) -> Eq EncodeParams
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: EncodeParams -> EncodeParams -> Bool
== :: EncodeParams -> EncodeParams -> Bool
$c/= :: EncodeParams -> EncodeParams -> Bool
/= :: EncodeParams -> EncodeParams -> Bool
Eq)

instance Default EncodeParams where
  def :: EncodeParams
  def :: EncodeParams
def = EncodeParams
defaultEncodeParams

defaultEncodeParams :: EncodeParams
defaultEncodeParams :: EncodeParams
defaultEncodeParams = FrameSize -> Threshold -> EncodeParams
EncodeParams FrameSize
forall a. Default a => a
def Threshold
forall a. Default a => a
def

-- | Number of bytes of uncompressed data.
newtype FrameSize = FrameSize Int
  deriving (Int -> FrameSize -> ShowS
[FrameSize] -> ShowS
FrameSize -> [Char]
(Int -> FrameSize -> ShowS)
-> (FrameSize -> [Char])
-> ([FrameSize] -> ShowS)
-> Show FrameSize
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> FrameSize -> ShowS
showsPrec :: Int -> FrameSize -> ShowS
$cshow :: FrameSize -> [Char]
show :: FrameSize -> [Char]
$cshowList :: [FrameSize] -> ShowS
showList :: [FrameSize] -> ShowS
Show, FrameSize -> FrameSize -> Bool
(FrameSize -> FrameSize -> Bool)
-> (FrameSize -> FrameSize -> Bool) -> Eq FrameSize
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: FrameSize -> FrameSize -> Bool
== :: FrameSize -> FrameSize -> Bool
$c/= :: FrameSize -> FrameSize -> Bool
/= :: FrameSize -> FrameSize -> Bool
Eq)

instance Default FrameSize where
    def :: FrameSize
    def :: FrameSize
def = FrameSize
defaultFrameSize

-- | The default frame size is 65536 bytes, which is the maximum allowed by the
-- [Snappy framing format
-- description](https://github.com/google/snappy/blob/main/framing_format.txt).
defaultFrameSize :: FrameSize
defaultFrameSize :: FrameSize
defaultFrameSize = Int -> FrameSize
FrameSize Int
snappySpecMaxChunkBytes

-- | See section 4.2 of the [Snappy framing format
-- description](https://github.com/google/snappy/blob/main/framing_format.txt).
snappySpecMaxChunkBytes :: Int
snappySpecMaxChunkBytes :: Int
snappySpecMaxChunkBytes = Int
65536

-- | Create a 'FrameSize'.
--
-- Must be within the inclusive range [ 1 .. 65536 ].
customFrameSize :: Int -> FrameSize
customFrameSize :: Int -> FrameSize
customFrameSize Int
n
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
1 Bool -> Bool -> Bool
&& Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
snappySpecMaxChunkBytes
    = Int -> FrameSize
FrameSize Int
n
    | Bool
otherwise
    = [Char] -> FrameSize
forall a. HasCallStack => [Char] -> a
error [Char]
"customFrameSize: invalid frame size"

-- | Unwrap a 'FrameSize'
unFrameSize :: FrameSize -> Int
unFrameSize :: FrameSize -> Int
unFrameSize (FrameSize Int
n) = Int
n

-- | Compression threshold, with explicit 'AlwaysCompress' and 'NeverCompress'
-- settings.
data Threshold =
      -- | Compress everything
      AlwaysCompress

      -- | Compress nothing
    | NeverCompress

      -- | Uncompressed size divided by compressed size.
      --
      -- Only produce compressed frames if the compression ratio for the data is
      -- equal to or above this threshold.
      --
      -- A higher threshold may result in less frames holding compressed data,
      -- and thus faster decompression/decoding.
      --
      -- [According to
      -- Google](https://github.com/google/snappy?tab=readme-ov-file#performance),
      -- the typical highest compression ratio that Snappy achieves is about 4,
      -- so a 'Ratio' of > 4.0 should be similar to 'NeverCompress', while a
      -- 'Ratio' of < 7/8 should be similar to 'AlwaysCompress'.
    | Ratio !Double
  deriving (Int -> Threshold -> ShowS
[Threshold] -> ShowS
Threshold -> [Char]
(Int -> Threshold -> ShowS)
-> (Threshold -> [Char])
-> ([Threshold] -> ShowS)
-> Show Threshold
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Threshold -> ShowS
showsPrec :: Int -> Threshold -> ShowS
$cshow :: Threshold -> [Char]
show :: Threshold -> [Char]
$cshowList :: [Threshold] -> ShowS
showList :: [Threshold] -> ShowS
Show, Threshold -> Threshold -> Bool
(Threshold -> Threshold -> Bool)
-> (Threshold -> Threshold -> Bool) -> Eq Threshold
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Threshold -> Threshold -> Bool
== :: Threshold -> Threshold -> Bool
$c/= :: Threshold -> Threshold -> Bool
/= :: Threshold -> Threshold -> Bool
Eq)

instance Default Threshold where
  def :: Threshold
  def :: Threshold
def = Threshold
defaultThreshold

-- | The default threshold is a ratio of 8:7, which was taken from the
-- [golang/snappy
-- implementation](https://github.com/golang/snappy/blob/43d5d4cd4e0e3390b0b645d5c3ef1187642403d8/encode.go#L231).
defaultThreshold :: Threshold
defaultThreshold :: Threshold
defaultThreshold = Double -> Threshold
Ratio (Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
0.875)

-- | Determines how much uncompressed data is stored in each resulting frame.
newtype EncodeState =
      EncodeState { EncodeState -> Int
encodeStateMaxChunkBytes :: Int }
  deriving Int -> EncodeState -> ShowS
[EncodeState] -> ShowS
EncodeState -> [Char]
(Int -> EncodeState -> ShowS)
-> (EncodeState -> [Char])
-> ([EncodeState] -> ShowS)
-> Show EncodeState
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> EncodeState -> ShowS
showsPrec :: Int -> EncodeState -> ShowS
$cshow :: EncodeState -> [Char]
show :: EncodeState -> [Char]
$cshowList :: [EncodeState] -> ShowS
showList :: [EncodeState] -> ShowS
Show

-- | A pair of frame-encoded chunks and an updated 'Encoder'.
data EncodeResult =
      EncodeResult
        { EncodeResult -> [ByteString]
encodeResultEncoded :: [Strict.ByteString]
        , EncodeResult -> Encoder
encodeResultEncoder :: !Encoder
        }
  deriving Int -> EncodeResult -> ShowS
[EncodeResult] -> ShowS
EncodeResult -> [Char]
(Int -> EncodeResult -> ShowS)
-> (EncodeResult -> [Char])
-> ([EncodeResult] -> ShowS)
-> Show EncodeResult
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> EncodeResult -> ShowS
showsPrec :: Int -> EncodeResult -> ShowS
$cshow :: EncodeResult -> [Char]
show :: EncodeResult -> [Char]
$cshowList :: [EncodeResult] -> ShowS
showList :: [EncodeResult] -> ShowS
Show

-- | Initialize an 'Encoder' with the given maximum number of bytes of
-- uncompressed data to include in frames resulting from the 'Encoder'. If the
-- given number of bytes is not in the inclusive range [1 .. 65536], 65536 is
-- used.
--
-- The 'Strict.ByteString' holds the Snappy stream identifier frame that must be
-- included at the start of every Snappy frame encoded stream.
initializeEncoder :: (Strict.ByteString, Encoder)
initializeEncoder :: (ByteString, Encoder)
initializeEncoder =
    ( ByteString
"\xff\x06\x00\00sNaPpY"
    , Encoder
        { encoderBuffer :: Buffer
encoderBuffer = Buffer
Buffer.empty
        }
    )

-- | Call to indicate no more input and flush the remaining data in the
-- 'Encoder' into a new frame.
--
-- If there is no more data in the 'Encoder', an empty list is returned.
--
-- * __Precondition:__ The buffer does not hold more data than the 'frameSize'
--   in the 'EncodeParams'. Use the postcondition of 'encodeBuffered' to ensure
--   this.
finalizeEncoder :: EncodeParams -> Encoder -> [Strict.ByteString]
finalizeEncoder :: EncodeParams -> Encoder -> [ByteString]
finalizeEncoder EncodeParams
ep (Encoder Buffer
b)
    | Buffer -> Bool
Buffer.null Buffer
b
    = []
    | Bool
otherwise
    = EncodeParams -> ByteString -> [ByteString]
encodeChunk EncodeParams
ep (Buffer -> ByteString
Buffer.toStrict Buffer
b)

-- | Fill and compress/encode as many frames as possible with the data in the
-- 'Encoder'.
--
-- /O(1)/ if there are not enough bytes in the buffer to fill a frame.
--
-- * __Postcondition:__ The resulting buffer never holds more than the
--   'frameSize' given in the 'EncodeParams'.
encodeBuffered :: EncodeParams -> Encoder -> EncodeResult
encodeBuffered :: EncodeParams -> Encoder -> EncodeResult
encodeBuffered ep :: EncodeParams
ep@EncodeParams{Threshold
FrameSize
frameSize :: EncodeParams -> FrameSize
threshold :: EncodeParams -> Threshold
frameSize :: FrameSize
threshold :: Threshold
..} = \(Encoder Buffer
b) ->
    [ByteString] -> Buffer -> EncodeResult
go [] Buffer
b
  where
    go :: [Strict.ByteString] -> Buffer -> EncodeResult
    go :: [ByteString] -> Buffer -> EncodeResult
go [ByteString]
acc Buffer
b =
        case Int -> Buffer -> Either Int (ByteString, Buffer)
Buffer.splitExactly (FrameSize -> Int
unFrameSize FrameSize
frameSize) Buffer
b of
          Right (ByteString
chunk, Buffer
b') ->
            [ByteString] -> Buffer -> EncodeResult
go ([ByteString] -> [ByteString]
forall a. [a] -> [a]
reverse (EncodeParams -> ByteString -> [ByteString]
encodeChunk EncodeParams
ep ByteString
chunk) [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ [ByteString]
acc) Buffer
b'
          Left Int
_ ->
            [ByteString] -> Encoder -> EncodeResult
EncodeResult
              ([ByteString] -> [ByteString]
forall a. [a] -> [a]
reverse [ByteString]
acc)
              (Buffer -> Encoder
Encoder Buffer
b)

-- | Encode the input as a potentially compressed Snappy frame.
--
-- This function takes a 'Strict.ByteString' because it must pass all of the
-- data to a C function which expects the data to sit in a single buffer.
encodeChunk ::
     EncodeParams
  -> Strict.ByteString
  -> [Strict.ByteString]
encodeChunk :: EncodeParams -> ByteString -> [ByteString]
encodeChunk EncodeParams{Threshold
FrameSize
frameSize :: EncodeParams -> FrameSize
threshold :: EncodeParams -> Threshold
frameSize :: FrameSize
threshold :: Threshold
..} ByteString
uncompressed =
    [ FrameHeader -> ByteString
encodeFrameHeader
        (FrameIdentifier -> Int -> FrameHeader
FrameHeader FrameIdentifier
frameId (ByteString -> Int
BS.Strict.length ByteString
payloadData Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4))
    , Checksum -> ByteString
Checksum.encode Checksum
maskedChecksum
    , ByteString
payloadData
    ]
  where
    maskedChecksum :: Checksum
    maskedChecksum :: Checksum
maskedChecksum = ByteString -> Checksum
Checksum.calculate ByteString
uncompressed

    compressed :: Strict.ByteString
    compressed :: ByteString
compressed = ByteString -> ByteString
Raw.compress ByteString
uncompressed

    compressionRatio :: Double
    compressionRatio :: Double
compressionRatio =
        forall a. Fractional a => a -> a -> a
(/) @Double
          (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> Int -> Double
forall a b. (a -> b) -> a -> b
$ ByteString -> Int
BS.Strict.length ByteString
uncompressed)
          (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> Int -> Double
forall a b. (a -> b) -> a -> b
$ ByteString -> Int
BS.Strict.length ByteString
compressed)

    (FrameIdentifier
frameId, ByteString
payloadData) =
        if Bool
doCompress then
          (FrameIdentifier
Compressed, ByteString
compressed)
        else
          (FrameIdentifier
Uncompressed, ByteString
uncompressed)

    doCompress :: Bool
doCompress =
        case Threshold
threshold of
          Threshold
AlwaysCompress -> Bool
True
          Threshold
NeverCompress  -> Bool
False
          Ratio Double
ratio    -> Double
compressionRatio Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
ratio

-------------------------------------------------------------------------------
-- Decoding Snappy frames
-------------------------------------------------------------------------------

-- | Buffers compressed data for decompression and holds some useful
-- decompression state.
data Decoder =
      Decoder
        { -- | Accumulated Snappy framed data.
          --
          -- * __Invariant:__ This buffer never holds a fully decodable Snappy
          --   frame.
          Decoder -> Buffer
decoderBuffer :: !Buffer

          -- | Tracks partial information about the buffer, e.g. whether we have
          -- decoded a header and how many bytes we need to fully decode a frame.
        , Decoder -> DecodeState
decoderState :: !DecodeState
        }
  deriving Int -> Decoder -> ShowS
[Decoder] -> ShowS
Decoder -> [Char]
(Int -> Decoder -> ShowS)
-> (Decoder -> [Char]) -> ([Decoder] -> ShowS) -> Show Decoder
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Decoder -> ShowS
showsPrec :: Int -> Decoder -> ShowS
$cshow :: Decoder -> [Char]
show :: Decoder -> [Char]
$cshowList :: [Decoder] -> ShowS
showList :: [Decoder] -> ShowS
Show

-- | Have we decoded a header for the current frame yet?
--
-- If so, what was that header?
data DecodeState =
      Initial
    | KnownHeader !FrameHeader
  deriving (Int -> DecodeState -> ShowS
[DecodeState] -> ShowS
DecodeState -> [Char]
(Int -> DecodeState -> ShowS)
-> (DecodeState -> [Char])
-> ([DecodeState] -> ShowS)
-> Show DecodeState
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DecodeState -> ShowS
showsPrec :: Int -> DecodeState -> ShowS
$cshow :: DecodeState -> [Char]
show :: DecodeState -> [Char]
$cshowList :: [DecodeState] -> ShowS
showList :: [DecodeState] -> ShowS
Show, DecodeState -> DecodeState -> Bool
(DecodeState -> DecodeState -> Bool)
-> (DecodeState -> DecodeState -> Bool) -> Eq DecodeState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DecodeState -> DecodeState -> Bool
== :: DecodeState -> DecodeState -> Bool
$c/= :: DecodeState -> DecodeState -> Bool
/= :: DecodeState -> DecodeState -> Bool
Eq)

-- | Pair of decompressed data chunks and an updated 'Decoder'.
data DecodeResult =
      DecodeResult
        { DecodeResult -> [ByteString]
decodeResultDecoded :: [Strict.ByteString]
        , DecodeResult -> Decoder
decodeResultDecoder :: !Decoder
        }
  deriving Int -> DecodeResult -> ShowS
[DecodeResult] -> ShowS
DecodeResult -> [Char]
(Int -> DecodeResult -> ShowS)
-> (DecodeResult -> [Char])
-> ([DecodeResult] -> ShowS)
-> Show DecodeResult
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DecodeResult -> ShowS
showsPrec :: Int -> DecodeResult -> ShowS
$cshow :: DecodeResult -> [Char]
show :: DecodeResult -> [Char]
$cshowList :: [DecodeResult] -> ShowS
showList :: [DecodeResult] -> ShowS
Show


-- | Decode parameters
data DecodeParams =
      DecodeParams
        { -- | Verify the uncompressed data checksums during decompression
          --
          -- Defaults to 'False'. Even if we don't verify the CRC, if the data
          -- is not Snappy compressed then decompression will likely still fail
          -- due to failing to decode the frame headers.
          --
          -- To enable this, use the incremental API
          -- ('Codec.Compression.SnappyC.Framed.decompressStep'). Note that
          -- checksum verification adds a significant overhead to decompression.
          DecodeParams -> Bool
verifyChecksum :: !Bool
        }
  deriving (Int -> DecodeParams -> ShowS
[DecodeParams] -> ShowS
DecodeParams -> [Char]
(Int -> DecodeParams -> ShowS)
-> (DecodeParams -> [Char])
-> ([DecodeParams] -> ShowS)
-> Show DecodeParams
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DecodeParams -> ShowS
showsPrec :: Int -> DecodeParams -> ShowS
$cshow :: DecodeParams -> [Char]
show :: DecodeParams -> [Char]
$cshowList :: [DecodeParams] -> ShowS
showList :: [DecodeParams] -> ShowS
Show, DecodeParams -> DecodeParams -> Bool
(DecodeParams -> DecodeParams -> Bool)
-> (DecodeParams -> DecodeParams -> Bool) -> Eq DecodeParams
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DecodeParams -> DecodeParams -> Bool
== :: DecodeParams -> DecodeParams -> Bool
$c/= :: DecodeParams -> DecodeParams -> Bool
/= :: DecodeParams -> DecodeParams -> Bool
Eq)

instance Default DecodeParams where
  def :: DecodeParams
  def :: DecodeParams
def = DecodeParams
defaultDecodeParams

-- | Default decode parameters
defaultDecodeParams :: DecodeParams
defaultDecodeParams :: DecodeParams
defaultDecodeParams = Bool -> DecodeParams
DecodeParams Bool
False

-- | Possible failure modes for decompression.
data DecodeFailure =
      DecompressionError Strict.ByteString
    | ReservedUnskippableFrameId Word8
    | BadStreamId Strict.ByteString
    | BadChecksum
        Strict.ByteString -- ^ Data
        Checksum -- ^ Received
        Checksum -- ^ Computed
    | NotDone
  deriving Int -> DecodeFailure -> ShowS
[DecodeFailure] -> ShowS
DecodeFailure -> [Char]
(Int -> DecodeFailure -> ShowS)
-> (DecodeFailure -> [Char])
-> ([DecodeFailure] -> ShowS)
-> Show DecodeFailure
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DecodeFailure -> ShowS
showsPrec :: Int -> DecodeFailure -> ShowS
$cshow :: DecodeFailure -> [Char]
show :: DecodeFailure -> [Char]
$cshowList :: [DecodeFailure] -> ShowS
showList :: [DecodeFailure] -> ShowS
Show
  deriving anyclass Show DecodeFailure
Typeable DecodeFailure
(Typeable DecodeFailure, Show DecodeFailure) =>
(DecodeFailure -> SomeException)
-> (SomeException -> Maybe DecodeFailure)
-> (DecodeFailure -> [Char])
-> Exception DecodeFailure
SomeException -> Maybe DecodeFailure
DecodeFailure -> [Char]
DecodeFailure -> SomeException
forall e.
(Typeable e, Show e) =>
(e -> SomeException)
-> (SomeException -> Maybe e) -> (e -> [Char]) -> Exception e
$ctoException :: DecodeFailure -> SomeException
toException :: DecodeFailure -> SomeException
$cfromException :: SomeException -> Maybe DecodeFailure
fromException :: SomeException -> Maybe DecodeFailure
$cdisplayException :: DecodeFailure -> [Char]
displayException :: DecodeFailure -> [Char]
Exception

-- | The empty 'Decoder', in an initial state.
initializeDecoder :: Decoder
initializeDecoder :: Decoder
initializeDecoder =
    Decoder
      { decoderBuffer :: Buffer
decoderBuffer = Buffer
Buffer.empty
      , decoderState :: DecodeState
decoderState = DecodeState
Initial
      }

-- | Verify that the 'Decoder' is complete.
--
-- If the 'Decoder'\'s buffer still has data in it, 'NotDone' is returned.
finalizeDecoder :: Decoder -> Either DecodeFailure ()
finalizeDecoder :: Decoder -> Either DecodeFailure ()
finalizeDecoder Decoder{Buffer
DecodeState
decoderBuffer :: Decoder -> Buffer
decoderState :: Decoder -> DecodeState
decoderBuffer :: Buffer
decoderState :: DecodeState
..}
    | DecodeState
decoderState DecodeState -> DecodeState -> Bool
forall a. Eq a => a -> a -> Bool
/= DecodeState
Initial Bool -> Bool -> Bool
|| Bool -> Bool
not (Buffer -> Bool
Buffer.null Buffer
decoderBuffer)
    = DecodeFailure -> Either DecodeFailure ()
forall a. DecodeFailure -> Either DecodeFailure a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError DecodeFailure
NotDone
    | Bool
otherwise
    = () -> Either DecodeFailure ()
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Decompress/decode as many frames as possible with the data in the
-- 'Decoder'.
--
-- This is not as lazy as it could be. If we had a version of 'decodeFrame' that
-- threw exceptions on failure, we could be a bit more lazy. It's not clear to
-- me if this would actually be good for performance.
--
-- /O(1)/ if there are insufficient bytes in the buffer.
decodeBuffered :: DecodeParams -> Decoder -> Either DecodeFailure DecodeResult
decodeBuffered :: DecodeParams -> Decoder -> Either DecodeFailure DecodeResult
decodeBuffered DecodeParams
dps =
    [ByteString] -> Decoder -> Either DecodeFailure DecodeResult
go []
  where
    go :: [Strict.ByteString] -> Decoder -> Either DecodeFailure DecodeResult
    go :: [ByteString] -> Decoder -> Either DecodeFailure DecodeResult
go [ByteString]
acc (Decoder Buffer
b state :: DecodeState
state@DecodeState
Initial) =
        case Int -> Buffer -> Either Int (ByteString, Buffer)
Buffer.splitExactly Int
4 Buffer
b of
          Right (ByteString
headerBs, Buffer
rest) -> do
            FrameHeader
header <- ByteString -> Either DecodeFailure FrameHeader
decodeHeader ByteString
headerBs
            [ByteString] -> Decoder -> Either DecodeFailure DecodeResult
go [ByteString]
acc (Buffer -> DecodeState -> Decoder
Decoder Buffer
rest (FrameHeader -> DecodeState
KnownHeader FrameHeader
header))
          Left Int
_ ->
            DecodeResult -> Either DecodeFailure DecodeResult
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (DecodeResult -> Either DecodeFailure DecodeResult)
-> DecodeResult -> Either DecodeFailure DecodeResult
forall a b. (a -> b) -> a -> b
$ [ByteString] -> Decoder -> DecodeResult
DecodeResult ([ByteString] -> [ByteString]
forall a. [a] -> [a]
reverse [ByteString]
acc) (Decoder -> DecodeResult) -> Decoder -> DecodeResult
forall a b. (a -> b) -> a -> b
$ Buffer -> DecodeState -> Decoder
Decoder Buffer
b DecodeState
state
    go [ByteString]
acc (Decoder Buffer
b state :: DecodeState
state@(KnownHeader FrameHeader
header)) =
        case Int -> Buffer -> Either Int (ByteString, Buffer)
Buffer.splitExactly (FrameHeader -> Int
frameHeaderPayloadSize FrameHeader
header) Buffer
b of
          Right (ByteString
payloadBs, Buffer
rest) -> do
            Maybe ByteString
uncompressed <- DecodeParams
-> FrameHeader
-> ByteString
-> Either DecodeFailure (Maybe ByteString)
decodeFrame DecodeParams
dps FrameHeader
header ByteString
payloadBs
            [ByteString] -> Decoder -> Either DecodeFailure DecodeResult
go ([ByteString]
-> (ByteString -> [ByteString]) -> Maybe ByteString -> [ByteString]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [ByteString]
acc (ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
: [ByteString]
acc) Maybe ByteString
uncompressed) (Buffer -> DecodeState -> Decoder
Decoder Buffer
rest DecodeState
Initial)
          Left Int
_ ->
            DecodeResult -> Either DecodeFailure DecodeResult
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (DecodeResult -> Either DecodeFailure DecodeResult)
-> DecodeResult -> Either DecodeFailure DecodeResult
forall a b. (a -> b) -> a -> b
$ [ByteString] -> Decoder -> DecodeResult
DecodeResult ([ByteString] -> [ByteString]
forall a. [a] -> [a]
reverse [ByteString]
acc) (Decoder -> DecodeResult) -> Decoder -> DecodeResult
forall a b. (a -> b) -> a -> b
$ Buffer -> DecodeState -> Decoder
Decoder Buffer
b DecodeState
state


-- | Decode header
--
-- __Precondition:__ The given 'Strict.ByteString' must be exactly 4 bytes
-- long.
decodeHeader :: Strict.ByteString -> Either DecodeFailure FrameHeader
decodeHeader :: ByteString -> Either DecodeFailure FrameHeader
decodeHeader ByteString
bs =
    case ByteString -> [Word8]
BS.Strict.unpack ByteString
bs of
      [Word8
bid, Word8
b1, Word8
b2, Word8
b3] ->
        let
          payloadLen :: Int
payloadLen = (Word8, Word8, Word8) -> Int
lEWord24BytesToInt (Word8
b1, Word8
b2, Word8
b3)
        in
          case Word8
bid of
            Word8
0xff -> FrameHeader -> Either DecodeFailure FrameHeader
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (FrameHeader -> Either DecodeFailure FrameHeader)
-> FrameHeader -> Either DecodeFailure FrameHeader
forall a b. (a -> b) -> a -> b
$ FrameIdentifier -> Int -> FrameHeader
FrameHeader FrameIdentifier
StreamId     Int
payloadLen
            Word8
0xfd -> FrameHeader -> Either DecodeFailure FrameHeader
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (FrameHeader -> Either DecodeFailure FrameHeader)
-> FrameHeader -> Either DecodeFailure FrameHeader
forall a b. (a -> b) -> a -> b
$ FrameIdentifier -> Int -> FrameHeader
FrameHeader FrameIdentifier
Padding      Int
payloadLen
            Word8
0x00 -> FrameHeader -> Either DecodeFailure FrameHeader
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (FrameHeader -> Either DecodeFailure FrameHeader)
-> FrameHeader -> Either DecodeFailure FrameHeader
forall a b. (a -> b) -> a -> b
$ FrameIdentifier -> Int -> FrameHeader
FrameHeader FrameIdentifier
Compressed   Int
payloadLen
            Word8
0x01 -> FrameHeader -> Either DecodeFailure FrameHeader
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (FrameHeader -> Either DecodeFailure FrameHeader)
-> FrameHeader -> Either DecodeFailure FrameHeader
forall a b. (a -> b) -> a -> b
$ FrameIdentifier -> Int -> FrameHeader
FrameHeader FrameIdentifier
Uncompressed Int
payloadLen
            Word8
fid
              | Word8
fid Word8 -> [Word8] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ Word8
0x02 .. Word8
0x7f ] ->
                  FrameHeader -> Either DecodeFailure FrameHeader
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (FrameHeader -> Either DecodeFailure FrameHeader)
-> FrameHeader -> Either DecodeFailure FrameHeader
forall a b. (a -> b) -> a -> b
$ FrameIdentifier -> Int -> FrameHeader
FrameHeader (Word8 -> FrameIdentifier
ReservedUnskippable Word8
fid) Int
payloadLen
              | Word8
fid Word8 -> [Word8] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ Word8
0x80 .. Word8
0xfd ] ->
                  FrameHeader -> Either DecodeFailure FrameHeader
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return (FrameHeader -> Either DecodeFailure FrameHeader)
-> FrameHeader -> Either DecodeFailure FrameHeader
forall a b. (a -> b) -> a -> b
$ FrameIdentifier -> Int -> FrameHeader
FrameHeader (Word8 -> FrameIdentifier
ReservedSkippable Word8
fid) Int
payloadLen
              | Bool
otherwise ->
                  [Char] -> Either DecodeFailure FrameHeader
forall a. HasCallStack => [Char] -> a
error ([Char] -> Either DecodeFailure FrameHeader)
-> [Char] -> Either DecodeFailure FrameHeader
forall a b. (a -> b) -> a -> b
$
                    [Char] -> Word8 -> [Char]
forall r. PrintfType r => [Char] -> r
printf
                      ( [Char]
"FrameFormat.decodeHeader: " [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++
                        [Char]
"impossible frame identifier 0x%x"
                      )
                      Word8
fid
      [Word8]
_ ->
        [Char] -> Either DecodeFailure FrameHeader
forall a. HasCallStack => [Char] -> a
error [Char]
"FrameFormat.decodeHeader: precondition violated"
  where
    lEWord24BytesToInt :: (Word8, Word8, Word8) -> Int
    lEWord24BytesToInt :: (Word8, Word8, Word8) -> Int
lEWord24BytesToInt (Word8
lsb, Word8
mid, Word8
msb) =
            Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
msb Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftL` Int
16
        Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
mid Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftL` Int
8
        Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
lsb

-- | Decode a frame
--
-- __Precondition:__ The given 'Strict.ByteString' is the payload associated
-- with the given 'FrameHeader'.
decodeFrame ::
     DecodeParams
  -> FrameHeader
  -> Strict.ByteString
  -> Either DecodeFailure (Maybe Strict.ByteString)
decodeFrame :: DecodeParams
-> FrameHeader
-> ByteString
-> Either DecodeFailure (Maybe ByteString)
decodeFrame DecodeParams
dps FrameHeader
header ByteString
bs =
    case FrameHeader -> FrameIdentifier
frameHeaderIdentifier FrameHeader
header of
      FrameIdentifier
StreamId ->
        if ByteString
bs ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
"sNaPpY" then
          Maybe ByteString -> Either DecodeFailure (Maybe ByteString)
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe ByteString
forall a. Maybe a
Nothing
        else
          DecodeFailure -> Either DecodeFailure (Maybe ByteString)
forall a. DecodeFailure -> Either DecodeFailure a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (DecodeFailure -> Either DecodeFailure (Maybe ByteString))
-> DecodeFailure -> Either DecodeFailure (Maybe ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString -> DecodeFailure
BadStreamId ByteString
bs
      FrameIdentifier
Compressed -> do
        let !(ByteString
checksumBs, ByteString
rest) = Int -> ByteString -> (ByteString, ByteString)
BS.Strict.splitAt Int
4 ByteString
bs
        ByteString
uncompressed <-
          case ByteString -> Maybe ByteString
Raw.decompress ByteString
rest of
            Maybe ByteString
Nothing -> DecodeFailure -> Either DecodeFailure ByteString
forall a. DecodeFailure -> Either DecodeFailure a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (DecodeFailure -> Either DecodeFailure ByteString)
-> DecodeFailure -> Either DecodeFailure ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> DecodeFailure
DecompressionError ByteString
rest
            Just ByteString
decompressed -> ByteString -> Either DecodeFailure ByteString
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
decompressed
        ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> Either DecodeFailure ByteString
-> Either DecodeFailure (Maybe ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DecodeParams
-> ByteString -> ByteString -> Either DecodeFailure ByteString
verifyPayload DecodeParams
dps ByteString
checksumBs ByteString
uncompressed
      FrameIdentifier
Uncompressed -> do
        let !(ByteString
checksumBs, ByteString
uncompressed) = Int -> ByteString -> (ByteString, ByteString)
BS.Strict.splitAt Int
4 ByteString
bs
        ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> Either DecodeFailure ByteString
-> Either DecodeFailure (Maybe ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DecodeParams
-> ByteString -> ByteString -> Either DecodeFailure ByteString
verifyPayload DecodeParams
dps ByteString
checksumBs ByteString
uncompressed
      FrameIdentifier
Padding ->
        Maybe ByteString -> Either DecodeFailure (Maybe ByteString)
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe ByteString
forall a. Maybe a
Nothing
      ReservedUnskippable Word8
fid ->
        -- An unskippable reserved frame is one that has an important payload,
        -- but we don't know what it is so we can't decode it.
        DecodeFailure -> Either DecodeFailure (Maybe ByteString)
forall a. DecodeFailure -> Either DecodeFailure a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (DecodeFailure -> Either DecodeFailure (Maybe ByteString))
-> DecodeFailure -> Either DecodeFailure (Maybe ByteString)
forall a b. (a -> b) -> a -> b
$ Word8 -> DecodeFailure
ReservedUnskippableFrameId Word8
fid
      ReservedSkippable Word8
_ ->
        Maybe ByteString -> Either DecodeFailure (Maybe ByteString)
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe ByteString
forall a. Maybe a
Nothing

-- | If checksum verification is enabled, compute the checksum and compare
-- against the decoded checksum.
verifyPayload ::
     DecodeParams
  -> Strict.ByteString
  -- ^ Encoded little-endian checksum
  -> Strict.ByteString
  -- ^ Uncompressed payload
  -> Either DecodeFailure Strict.ByteString
verifyPayload :: DecodeParams
-> ByteString -> ByteString -> Either DecodeFailure ByteString
verifyPayload DecodeParams
dps ByteString
checksumBs ByteString
uncompressed = do
    Bool -> Either DecodeFailure () -> Either DecodeFailure ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (DecodeParams -> Bool
verifyChecksum DecodeParams
dps) (Either DecodeFailure () -> Either DecodeFailure ())
-> Either DecodeFailure () -> Either DecodeFailure ()
forall a b. (a -> b) -> a -> b
$ do
      let
        decodedChecksum :: Checksum
decodedChecksum = ByteString -> Checksum
Checksum.decode ByteString
checksumBs
        computedChecksum :: Checksum
computedChecksum = ByteString -> Checksum
Checksum.calculate ByteString
uncompressed
      Bool -> Either DecodeFailure () -> Either DecodeFailure ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Checksum
computedChecksum Checksum -> Checksum -> Bool
forall a. Eq a => a -> a -> Bool
/= Checksum
decodedChecksum) (Either DecodeFailure () -> Either DecodeFailure ())
-> Either DecodeFailure () -> Either DecodeFailure ()
forall a b. (a -> b) -> a -> b
$
        DecodeFailure -> Either DecodeFailure ()
forall a. DecodeFailure -> Either DecodeFailure a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (DecodeFailure -> Either DecodeFailure ())
-> DecodeFailure -> Either DecodeFailure ()
forall a b. (a -> b) -> a -> b
$
          ByteString -> Checksum -> Checksum -> DecodeFailure
BadChecksum ByteString
uncompressed Checksum
decodedChecksum Checksum
computedChecksum
    ByteString -> Either DecodeFailure ByteString
forall a. a -> Either DecodeFailure a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
uncompressed