content: {-# LANGUAGE LambdaCase #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Simplex.Messaging.Compression where
  
  import qualified Codec.Compression.Zstd as Z1
  import Data.ByteString (ByteString)
  import qualified Data.ByteString as B
  import Simplex.Messaging.Encoding
  
  data Compressed
    = -- | Short messages are left intact to skip copying and FFI festivities.
      Passthrough ByteString
    | -- | Generic compression using no extra context.
      Compressed Large
  
  -- | Messages below this length are not encoded to avoid compression overhead.
  maxLengthPassthrough :: Int
  maxLengthPassthrough = 180 -- Sampled from real client data. Messages with length > 180 rapidly gain compression ratio.
  
  compressionLevel :: Num a => a
  compressionLevel = 3
  
  instance Encoding Compressed where
    smpEncode = \case
      Passthrough bytes -> "0" <> smpEncode bytes
      Compressed bytes -> "1" <> smpEncode bytes
    smpP =
      smpP >>= \case
        '0' -> Passthrough <$> smpP
        '1' -> Compressed <$> smpP
        x -> fail $ "unknown Compressed tag: " <> show x
  
  compress1 :: ByteString -> Compressed
  compress1 bs
    | B.length bs <= maxLengthPassthrough = Passthrough bs
    | otherwise = Compressed . Large $ Z1.compress compressionLevel bs
  
  decompress1 :: Compressed -> Either String ByteString
  decompress1 = \case
    Passthrough bs -> Right bs
    Compressed (Large bs) -> case Z1.decompress bs of
      Z1.Error e -> Left e
      Z1.Skip -> Right mempty
      Z1.Decompress bs' -> Right bs'
  