-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in
-- the LICENSE file in the root directory of this source tree. An
-- additional grant of patent rights can be found in the PATENTS file
-- in the same directory.

-- |
-- Module      : Codec.Compression.Zstd.Base
-- Copyright   : (c) 2016-present, Facebook, Inc. All rights reserved.
--
-- License     : BSD3
-- Maintainer  : bryano@fb.com
-- Stability   : experimental
-- Portability : GHC
--
-- Mid-level bindings to the native zstd compression library.  These
-- bindings provide a little more safety and ease of use than the
-- lowest-level FFI bindings.  Unless you have highly specialized
-- needs, you should use the streaming or other higher-level APIs
-- instead.

module Codec.Compression.Zstd.Base
    (
    -- * One-shot functions
      compress
    , maxCLevel
    , decompress
    , getDecompressedSize

    -- ** Cheaper operations using contexts
    -- *** Compression
    , CCtx
    , withCCtx
    , compressCCtx

    -- *** Decompression
    , DCtx
    , withDCtx
    , decompressDCtx

    -- * Streaming operations
    -- ** Streaming types
    , CStream
    , DStream
    , FFI.Buffer(..)
    , FFI.In
    , FFI.Out

    -- ** Streaming compression
    , cstreamInSize
    , cstreamOutSize
    , createCStream
    , initCStream
    , compressStream
    , endStream

    -- ** Streaming decompression
    , dstreamInSize
    , dstreamOutSize
    , createDStream
    , initDStream
    , decompressStream

    -- * Dictionary compression
    , trainFromBuffer
    , getDictID

    , compressUsingDict
    , decompressUsingDict

    -- ** Pre-digested dictionaries
    -- *** Compression
    , CDict
    , createCDict
    , compressUsingCDict

    -- *** Decompression
    , DDict
    , createDDict
    , decompressUsingDDict
    ) where

import Codec.Compression.Zstd.Base.Types (CDict(..), DDict(..))
import Codec.Compression.Zstd.FFI.Types (CCtx, DCtx)
import Control.Exception.Base (bracket)
import Data.Word (Word, Word64)
import Foreign.C.Types (CSize)
import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr)
import Foreign.Ptr (Ptr, castPtr)
import qualified Codec.Compression.Zstd.FFI as FFI

-- | Compress bytes from source buffer into destination buffer.
-- The destination buffer must be already allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
compress :: Ptr dst         -- ^ Destination buffer.
         -> Int             -- ^ Capacity of destination buffer.
         -> Ptr src         -- ^ Source buffer.
         -> Int             -- ^ Size of source buffer.
         -> Int             -- ^ Compression level.
         -> IO (Either String Int)
compress :: Ptr dst -> Int -> Ptr src -> Int -> Int -> IO (Either String Int)
compress Ptr dst
dst Int
dstSize Ptr src
src Int
srcSize Int
level = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  Ptr dst -> CSize -> Ptr src -> CSize -> CInt -> IO CSize
forall dst src.
Ptr dst -> CSize -> Ptr src -> CSize -> CInt -> IO CSize
FFI.compress Ptr dst
dst (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize) Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize)
               (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
level)

-- | The maximum compression level supported by the library.
maxCLevel :: Int
maxCLevel :: Int
maxCLevel = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
FFI.maxCLevel

-- | Decompress a buffer.  The destination buffer must be already
-- allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
decompress :: Ptr dst         -- ^ Destination buffer.
           -> Int             -- ^ Capacity of destination buffer.
           -> Ptr src         -- ^ Source buffer.
           -> Int
           -- ^ Size of compressed input.  This must be exact, so
           -- for example supplying the size of a buffer that is
           -- larger than the compressed input will cause a failure.
           -> IO (Either String Int)
decompress :: Ptr dst -> Int -> Ptr src -> Int -> IO (Either String Int)
decompress Ptr dst
dst Int
dstSize Ptr src
src Int
srcSize = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  Ptr dst -> CSize -> Ptr src -> CSize -> IO CSize
forall dst src. Ptr dst -> CSize -> Ptr src -> CSize -> IO CSize
FFI.decompress Ptr dst
dst (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize) Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize)

-- | Returns the decompressed size of a compressed payload if known.
--
-- To discover precisely why a result is not known, follow up with
-- 'FFI.getFrameParams'.
getDecompressedSize :: Ptr src
                    -> Int
                    -> IO (Maybe Word64)
getDecompressedSize :: Ptr src -> Int -> IO (Maybe Word64)
getDecompressedSize Ptr src
src Int
srcSize = do
  CULLong
ret <- Ptr src -> CSize -> IO CULLong
forall src. Ptr src -> CSize -> IO CULLong
FFI.getDecompressedSize Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize)
  Maybe Word64 -> IO (Maybe Word64)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Word64 -> IO (Maybe Word64))
-> Maybe Word64 -> IO (Maybe Word64)
forall a b. (a -> b) -> a -> b
$! if CULLong
ret CULLong -> CULLong -> Bool
forall a. Eq a => a -> a -> Bool
== CULLong
0
            then Maybe Word64
forall a. Maybe a
Nothing
            else Word64 -> Maybe Word64
forall a. a -> Maybe a
Just (CULLong -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral CULLong
ret)

-- | Allocate a compression context, run an action that may reuse the
-- context as many times as it needs, then free the context.
withCCtx :: (Ptr CCtx -> IO a) -> IO a
withCCtx :: (Ptr CCtx -> IO a) -> IO a
withCCtx Ptr CCtx -> IO a
act =
  IO (Ptr CCtx) -> (Ptr CCtx -> IO ()) -> (Ptr CCtx -> IO a) -> IO a
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket (String -> IO (Ptr CCtx) -> IO (Ptr CCtx)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
FFI.checkAlloc String
"withCCtx" IO (Ptr CCtx)
FFI.createCCtx) Ptr CCtx -> IO ()
FFI.freeCCtx Ptr CCtx -> IO a
act

-- | Compress bytes from source buffer into destination buffer.
-- The destination buffer must be already allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
compressCCtx :: Ptr CCtx    -- ^ Compression context.
             -> Ptr dst     -- ^ Destination buffer.
             -> Int         -- ^ Capacity of destination buffer.
             -> Ptr src     -- ^ Source buffer.
             -> CSize       -- ^ Size of source buffer.
             -> Int         -- ^ Compression level.
             -> IO (Either String Int)
compressCCtx :: Ptr CCtx
-> Ptr dst
-> Int
-> Ptr src
-> CSize
-> Int
-> IO (Either String Int)
compressCCtx Ptr CCtx
cctx Ptr dst
dstPtr Int
dstSize Ptr src
srcPtr CSize
srcSize Int
level = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  Ptr CCtx
-> Ptr dst -> CSize -> Ptr src -> CSize -> CInt -> IO CSize
forall dst src.
Ptr CCtx
-> Ptr dst -> CSize -> Ptr src -> CSize -> CInt -> IO CSize
FFI.compressCCtx Ptr CCtx
cctx Ptr dst
dstPtr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize)
                        Ptr src
srcPtr (CSize -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
srcSize)
                        (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
level)

-- | Allocate a decompression context, run an action that may reuse
-- the context as many times as it needs, then free the context.
withDCtx :: (Ptr DCtx -> IO a) -> IO a
withDCtx :: (Ptr DCtx -> IO a) -> IO a
withDCtx Ptr DCtx -> IO a
act =
  IO (Ptr DCtx) -> (Ptr DCtx -> IO ()) -> (Ptr DCtx -> IO a) -> IO a
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket (String -> IO (Ptr DCtx) -> IO (Ptr DCtx)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
FFI.checkAlloc String
"withDCtx" IO (Ptr DCtx)
FFI.createDCtx) Ptr DCtx -> IO ()
FFI.freeDCtx Ptr DCtx -> IO a
act

-- | Decompress a buffer.  The destination buffer must be already
-- allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
decompressDCtx :: Ptr DCtx        -- ^ Decompression context.
               -> Ptr dst         -- ^ Destination buffer.
               -> Int             -- ^ Capacity of destination buffer.
               -> Ptr src         -- ^ Source buffer.
               -> Int
               -- ^ Size of compressed input.  This must be exact, so
               -- for example supplying the size of a buffer that is
               -- larger than the compressed input will cause a failure.
               -> IO (Either String Int)
decompressDCtx :: Ptr DCtx
-> Ptr dst -> Int -> Ptr src -> Int -> IO (Either String Int)
decompressDCtx Ptr DCtx
dctx Ptr dst
dst Int
dstSize Ptr src
src Int
srcSize = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  Ptr DCtx -> Ptr dst -> CSize -> Ptr src -> CSize -> IO CSize
forall dst src.
Ptr DCtx -> Ptr dst -> CSize -> Ptr src -> CSize -> IO CSize
FFI.decompressDCtx Ptr DCtx
dctx Ptr dst
dst (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize) Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize)

-- | Recommended size for input buffer.
cstreamInSize :: Int
cstreamInSize :: Int
cstreamInSize = CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
FFI.cstreamInSize

-- | Recommended size for output buffer.
cstreamOutSize :: Int
cstreamOutSize :: Int
cstreamOutSize = CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
FFI.cstreamOutSize

-- | A context for streaming compression.
newtype CStream = CS (ForeignPtr FFI.CStream)

-- | Create a 'CStream' value.  After use, this will eventually be
-- freed via a finalizer.
createCStream :: IO CStream
createCStream :: IO CStream
createCStream = do
  Ptr CStream
cs <- String -> IO (Ptr CStream) -> IO (Ptr CStream)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
FFI.checkAlloc String
"createCStream" IO (Ptr CStream)
FFI.createCStream
  ForeignPtr CStream
cfp <- FinalizerPtr CStream -> Ptr CStream -> IO (ForeignPtr CStream)
forall a. FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
newForeignPtr FinalizerPtr CStream
FFI.p_freeCStream Ptr CStream
cs
  CStream -> IO CStream
forall (m :: * -> *) a. Monad m => a -> m a
return (ForeignPtr CStream -> CStream
CS ForeignPtr CStream
cfp)

-- | Begin a new streaming compression operation.
initCStream :: CStream
            -> Int              -- ^ Compression level.
            -> IO (Either String ())
initCStream :: CStream -> Int -> IO (Either String ())
initCStream (CS ForeignPtr CStream
cfp) Int
level =
  (Either String Int -> Either String ())
-> IO (Either String Int) -> IO (Either String ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Int -> ()) -> Either String Int -> Either String ()
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (() -> Int -> ()
forall a b. a -> b -> a
const ())) (IO (Either String Int) -> IO (Either String ()))
-> IO (Either String Int) -> IO (Either String ())
forall a b. (a -> b) -> a -> b
$ IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$ ForeignPtr CStream -> (Ptr CStream -> IO CSize) -> IO CSize
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CStream
cfp ((Ptr CStream -> IO CSize) -> IO CSize)
-> (Ptr CStream -> IO CSize) -> IO CSize
forall a b. (a -> b) -> a -> b
$ \Ptr CStream
cs ->
    Ptr CStream -> CInt -> IO CSize
FFI.initCStream Ptr CStream
cs (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
level)

-- | Consume part or all of an input.
compressStream :: CStream -> Ptr (FFI.Buffer FFI.Out) -> Ptr (FFI.Buffer FFI.In)
               -> IO (Either String Int)
compressStream :: CStream
-> Ptr (Buffer Out) -> Ptr (Buffer In) -> IO (Either String Int)
compressStream (CS ForeignPtr CStream
cfp) Ptr (Buffer Out)
bi Ptr (Buffer In)
bo = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  ForeignPtr CStream -> (Ptr CStream -> IO CSize) -> IO CSize
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CStream
cfp ((Ptr CStream -> IO CSize) -> IO CSize)
-> (Ptr CStream -> IO CSize) -> IO CSize
forall a b. (a -> b) -> a -> b
$ \Ptr CStream
cs ->
    Ptr CStream -> Ptr (Buffer Out) -> Ptr (Buffer In) -> IO CSize
FFI.compressStream Ptr CStream
cs Ptr (Buffer Out)
bi Ptr (Buffer In)
bo

-- | End a compression stream. This performs a flush and writes a
-- frame epilogue.
endStream :: CStream -> Ptr (FFI.Buffer FFI.Out) -> IO (Either String Int)
endStream :: CStream -> Ptr (Buffer Out) -> IO (Either String Int)
endStream (CS ForeignPtr CStream
cfp) Ptr (Buffer Out)
bo = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  ForeignPtr CStream -> (Ptr CStream -> IO CSize) -> IO CSize
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CStream
cfp ((Ptr CStream -> IO CSize) -> IO CSize)
-> (Ptr CStream -> IO CSize) -> IO CSize
forall a b. (a -> b) -> a -> b
$ \Ptr CStream
cs ->
    Ptr CStream -> Ptr (Buffer Out) -> IO CSize
FFI.endStream Ptr CStream
cs Ptr (Buffer Out)
bo

-- | Recommended size for input buffer.
dstreamInSize :: Int
dstreamInSize :: Int
dstreamInSize = CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
FFI.dstreamInSize

-- | Recommended size for output buffer.
dstreamOutSize :: Int
dstreamOutSize :: Int
dstreamOutSize = CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
FFI.dstreamOutSize

-- | A context for streaming decompression.
newtype DStream = DS (ForeignPtr FFI.DStream)

-- | Create a streaming decompression context.  After use, this will
-- eventually be freed via a finalizer.
createDStream :: IO DStream
createDStream :: IO DStream
createDStream = do
  Ptr DStream
ds <- String -> IO (Ptr DStream) -> IO (Ptr DStream)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
FFI.checkAlloc String
"createDStream" IO (Ptr DStream)
FFI.createDStream
  ForeignPtr DStream
dfp <- FinalizerPtr DStream -> Ptr DStream -> IO (ForeignPtr DStream)
forall a. FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
newForeignPtr FinalizerPtr DStream
FFI.p_freeDStream Ptr DStream
ds
  DStream -> IO DStream
forall (m :: * -> *) a. Monad m => a -> m a
return (ForeignPtr DStream -> DStream
DS ForeignPtr DStream
dfp)

-- | Begin a new streaming decompression operation.
initDStream :: DStream
            -> IO (Either String ())
initDStream :: DStream -> IO (Either String ())
initDStream (DS ForeignPtr DStream
dfp) =
  (Either String Int -> Either String ())
-> IO (Either String Int) -> IO (Either String ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Int -> ()) -> Either String Int -> Either String ()
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (() -> Int -> ()
forall a b. a -> b -> a
const ())) (IO (Either String Int) -> IO (Either String ()))
-> IO (Either String Int) -> IO (Either String ())
forall a b. (a -> b) -> a -> b
$ IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$ ForeignPtr DStream -> (Ptr DStream -> IO CSize) -> IO CSize
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr DStream
dfp Ptr DStream -> IO CSize
FFI.initDStream

-- | Consume part or all of an input.
decompressStream :: DStream -> Ptr (FFI.Buffer FFI.Out)
                 -> Ptr (FFI.Buffer FFI.In) -> IO (Either String Int)
decompressStream :: DStream
-> Ptr (Buffer Out) -> Ptr (Buffer In) -> IO (Either String Int)
decompressStream (DS ForeignPtr DStream
dfp) Ptr (Buffer Out)
bi Ptr (Buffer In)
bo = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  ForeignPtr DStream -> (Ptr DStream -> IO CSize) -> IO CSize
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr DStream
dfp ((Ptr DStream -> IO CSize) -> IO CSize)
-> (Ptr DStream -> IO CSize) -> IO CSize
forall a b. (a -> b) -> a -> b
$ \Ptr DStream
ds ->
    Ptr DStream -> Ptr (Buffer Out) -> Ptr (Buffer In) -> IO CSize
FFI.decompressStream Ptr DStream
ds Ptr (Buffer Out)
bi Ptr (Buffer In)
bo

-- | Train a dictionary from a collection of samples.
-- Returns the number size of the resulting dictionary.
trainFromBuffer :: Ptr dict
                -- ^ Preallocated dictionary buffer.
                -> Int
                -- ^ Capacity of dictionary buffer.
                -> Ptr samples
                -- ^ Concatenated samples.
                -> Ptr Int
                -- ^ Array of sizes of samples.
                -> Int
                -- ^ Number of samples.
                -> IO (Either String Int)
trainFromBuffer :: Ptr dict
-> Int -> Ptr samples -> Ptr Int -> Int -> IO (Either String Int)
trainFromBuffer Ptr dict
dictPtr Int
dictSize Ptr samples
sampPtr Ptr Int
sampSizes Int
sampCount = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  Ptr dict -> CSize -> Ptr samples -> Ptr CSize -> CUInt -> IO CSize
forall dict samples.
Ptr dict -> CSize -> Ptr samples -> Ptr CSize -> CUInt -> IO CSize
FFI.trainFromBuffer Ptr dict
dictPtr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dictSize)
                      Ptr samples
sampPtr (Ptr Int -> Ptr CSize
forall a b. Ptr a -> Ptr b
castPtr Ptr Int
sampSizes) (Int -> CUInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
sampCount)

-- | Return the identifier for the given dictionary, or 'Nothing' if
-- not a valid dictionary.
getDictID :: Ptr dict -> Int -> IO (Maybe Word)
getDictID :: Ptr dict -> Int -> IO (Maybe Word)
getDictID Ptr dict
ptr Int
size = do
  CUInt
n <- Ptr dict -> CSize -> IO CUInt
forall dict. Ptr dict -> CSize -> IO CUInt
FFI.getDictID Ptr dict
ptr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size)
  Maybe Word -> IO (Maybe Word)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Word -> IO (Maybe Word)) -> Maybe Word -> IO (Maybe Word)
forall a b. (a -> b) -> a -> b
$! if CUInt
n CUInt -> CUInt -> Bool
forall a. Eq a => a -> a -> Bool
== CUInt
0
            then Maybe Word
forall a. Maybe a
Nothing
            else Word -> Maybe Word
forall a. a -> Maybe a
Just (CUInt -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral CUInt
n)

-- | Compress bytes from source buffer into destination buffer.
-- The destination buffer must be already allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
compressUsingDict :: Ptr CCtx
                  -> Ptr dst         -- ^ Destination buffer.
                  -> Int             -- ^ Capacity of destination buffer.
                  -> Ptr src         -- ^ Source buffer.
                  -> Int             -- ^ Size of source buffer.
                  -> Ptr dict        -- ^ Dictionary.
                  -> Int             -- ^ Size of dictionary.
                  -> Int             -- ^ Compression level.
                  -> IO (Either String Int)
compressUsingDict :: Ptr CCtx
-> Ptr dst
-> Int
-> Ptr src
-> Int
-> Ptr dict
-> Int
-> Int
-> IO (Either String Int)
compressUsingDict Ptr CCtx
ctx Ptr dst
dst Int
dstSize Ptr src
src Int
srcSize Ptr dict
dict Int
dictSize Int
level = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  Ptr CCtx
-> Ptr dst
-> CSize
-> Ptr src
-> CSize
-> Ptr dict
-> CSize
-> CInt
-> IO CSize
forall dst src dict.
Ptr CCtx
-> Ptr dst
-> CSize
-> Ptr src
-> CSize
-> Ptr dict
-> CSize
-> CInt
-> IO CSize
FFI.compressUsingDict Ptr CCtx
ctx Ptr dst
dst (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize)
    Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize) Ptr dict
dict (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dictSize) (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
level)

-- | Decompress a buffer.  The destination buffer must be already
-- allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
decompressUsingDict :: Ptr DCtx
                    -> Ptr dst         -- ^ Destination buffer.
                    -> Int             -- ^ Capacity of destination buffer.
                    -> Ptr src         -- ^ Source buffer.
                    -> Int
                    -- ^ Size of compressed input.  This must be exact, so
                    -- for example supplying the size of a buffer that is
                    -- larger than the compressed input will cause a failure.
                    -> Ptr dict        -- ^ Dictionary.
                    -> Int             -- ^ Size of dictionary.
                    -> IO (Either String Int)
decompressUsingDict :: Ptr DCtx
-> Ptr dst
-> Int
-> Ptr src
-> Int
-> Ptr dict
-> Int
-> IO (Either String Int)
decompressUsingDict Ptr DCtx
ctx Ptr dst
dst Int
dstSize Ptr src
src Int
srcSize Ptr dict
dict Int
dictSize = IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> IO CSize -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$
  Ptr DCtx
-> Ptr dst
-> CSize
-> Ptr src
-> CSize
-> Ptr dict
-> CSize
-> IO CSize
forall dst src dict.
Ptr DCtx
-> Ptr dst
-> CSize
-> Ptr src
-> CSize
-> Ptr dict
-> CSize
-> IO CSize
FFI.decompressUsingDict Ptr DCtx
ctx Ptr dst
dst (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize)
    Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize) Ptr dict
dict (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dictSize)

-- | Create a pre-digested compression dictionary.  After use, this
-- will eventually be freed via a finalizer.
createCDict :: Ptr dict
            -- ^ Dictionary.
            -> Int
            -- ^ Size of dictionary.
            -> Int
            -- ^ Compression level.
            -> IO CDict
createCDict :: Ptr dict -> Int -> Int -> IO CDict
createCDict Ptr dict
dict Int
size Int
level = do
  Ptr CDict
cd <- String -> IO (Ptr CDict) -> IO (Ptr CDict)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
FFI.checkAlloc String
"createCDict" (IO (Ptr CDict) -> IO (Ptr CDict))
-> IO (Ptr CDict) -> IO (Ptr CDict)
forall a b. (a -> b) -> a -> b
$
        Ptr dict -> CSize -> CInt -> IO (Ptr CDict)
forall dict. Ptr dict -> CSize -> CInt -> IO (Ptr CDict)
FFI.createCDict Ptr dict
dict (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size) (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
level)
  ForeignPtr CDict
fp <- FinalizerPtr CDict -> Ptr CDict -> IO (ForeignPtr CDict)
forall a. FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
newForeignPtr FinalizerPtr CDict
FFI.p_freeCDict Ptr CDict
cd
  CDict -> IO CDict
forall (m :: * -> *) a. Monad m => a -> m a
return (ForeignPtr CDict -> CDict
CD ForeignPtr CDict
fp)

-- | Compress bytes from source buffer into destination buffer, using
-- a pre-digested dictionary.  The destination buffer must be already
-- allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
compressUsingCDict
    :: Ptr CCtx    -- ^ Compression context.
    -> Ptr dst     -- ^ Destination buffer.
    -> Int         -- ^ Capacity of destination buffer.
    -> Ptr src     -- ^ Source buffer.
    -> Int         -- ^ Size of source buffer.
    -> CDict       -- ^ Dictionary.
    -> IO (Either String Int)
compressUsingCDict :: Ptr CCtx
-> Ptr dst
-> Int
-> Ptr src
-> Int
-> CDict
-> IO (Either String Int)
compressUsingCDict Ptr CCtx
ctx Ptr dst
dst Int
dstSize Ptr src
src Int
srcSize (CD ForeignPtr CDict
fp) =
  IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> ((Ptr CDict -> IO CSize) -> IO CSize)
-> (Ptr CDict -> IO CSize)
-> IO (Either String Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ForeignPtr CDict -> (Ptr CDict -> IO CSize) -> IO CSize
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDict
fp ((Ptr CDict -> IO CSize) -> IO (Either String Int))
-> (Ptr CDict -> IO CSize) -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$ \ Ptr CDict
dict ->
  Ptr CCtx
-> Ptr dst -> CSize -> Ptr src -> CSize -> Ptr CDict -> IO CSize
forall dst src.
Ptr CCtx
-> Ptr dst -> CSize -> Ptr src -> CSize -> Ptr CDict -> IO CSize
FFI.compressUsingCDict Ptr CCtx
ctx Ptr dst
dst (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize)
    Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize) Ptr CDict
dict

-- | Create a pre-digested decompression dictionary.  After use, this
-- will eventually be freed via a finalizer.
createDDict :: Ptr dict
            -- ^ Dictionary.
            -> Int
            -- ^ Size of dictionary.
            -> IO DDict
createDDict :: Ptr dict -> Int -> IO DDict
createDDict Ptr dict
dict Int
size = do
  Ptr DDict
cd <- String -> IO (Ptr DDict) -> IO (Ptr DDict)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
FFI.checkAlloc String
"createDDict" (IO (Ptr DDict) -> IO (Ptr DDict))
-> IO (Ptr DDict) -> IO (Ptr DDict)
forall a b. (a -> b) -> a -> b
$
        Ptr dict -> CSize -> IO (Ptr DDict)
forall dict. Ptr dict -> CSize -> IO (Ptr DDict)
FFI.createDDict Ptr dict
dict (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size)
  ForeignPtr DDict
fp <- FinalizerPtr DDict -> Ptr DDict -> IO (ForeignPtr DDict)
forall a. FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
newForeignPtr FinalizerPtr DDict
FFI.p_freeDDict Ptr DDict
cd
  DDict -> IO DDict
forall (m :: * -> *) a. Monad m => a -> m a
return (ForeignPtr DDict -> DDict
DD ForeignPtr DDict
fp)

-- | Decompress bytes from source buffer into destination buffer,
-- using a pre-digested dictionary.  The destination buffer must be
-- already allocated.
--
-- Returns the number of bytes written into destination buffer, or an
-- error description if it fails.
decompressUsingDDict
    :: Ptr DCtx    -- ^ Compression context.
    -> Ptr dst     -- ^ Destination buffer.
    -> Int         -- ^ Capacity of destination buffer.
    -> Ptr src     -- ^ Source buffer.
    -> Int         -- ^ Size of source buffer.
    -> DDict       -- ^ Dictionary.
    -> IO (Either String Int)
decompressUsingDDict :: Ptr DCtx
-> Ptr dst
-> Int
-> Ptr src
-> Int
-> DDict
-> IO (Either String Int)
decompressUsingDDict Ptr DCtx
ctx Ptr dst
dst Int
dstSize Ptr src
src Int
srcSize (DD ForeignPtr DDict
fp) =
  IO CSize -> IO (Either String Int)
checkError (IO CSize -> IO (Either String Int))
-> ((Ptr DDict -> IO CSize) -> IO CSize)
-> (Ptr DDict -> IO CSize)
-> IO (Either String Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ForeignPtr DDict -> (Ptr DDict -> IO CSize) -> IO CSize
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr DDict
fp ((Ptr DDict -> IO CSize) -> IO (Either String Int))
-> (Ptr DDict -> IO CSize) -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$ \ Ptr DDict
dict ->
  Ptr DCtx
-> Ptr dst -> CSize -> Ptr src -> CSize -> Ptr DDict -> IO CSize
forall dst src.
Ptr DCtx
-> Ptr dst -> CSize -> Ptr src -> CSize -> Ptr DDict -> IO CSize
FFI.decompressUsingDDict Ptr DCtx
ctx Ptr dst
dst (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dstSize)
    Ptr src
src (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
srcSize) Ptr DDict
dict

checkError :: IO CSize -> IO (Either String Int)
checkError :: IO CSize -> IO (Either String Int)
checkError IO CSize
act = do
  CSize
ret <- IO CSize
act
  Either String Int -> IO (Either String Int)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String Int -> IO (Either String Int))
-> Either String Int -> IO (Either String Int)
forall a b. (a -> b) -> a -> b
$! if CSize -> Bool
FFI.isError CSize
ret
            then String -> Either String Int
forall a b. a -> Either a b
Left (CSize -> String
FFI.getErrorName CSize
ret)
            else Int -> Either String Int
forall a b. b -> Either a b
Right (CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
ret)