    {-# LANGUAGE CPP #-}
    {-# LANGUAGE DefaultSignatures #-}
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE TypeOperators #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE UndecidableInstances #-}
    --------------------------------------------------------------------
    -- |
    -- Copyright :  (c) Edward Kmett 2013-2015
    -- License   :  BSD3
    -- Maintainer:  Edward Kmett <ekmett@gmail.com>
    -- Stability :  experimental
    -- Portability: non-portable
    --
    -- This module generalizes the @binary@ 'B.PutM' and @cereal@ 'S.PutM'
    -- monads in an ad hoc fashion to permit code to be written that is
    -- compatible across them.
    --
    -- Moreover, this class permits code to be written to be portable over
    -- various monad transformers applied to these as base monads.
    --------------------------------------------------------------------
    module Data.Bytes.Put
      ( MonadPut(..)
      , runPutL
      , runPutS
      ) where

    import Control.Monad.Trans.Class (MonadTrans(..))
    import Control.Monad.Trans.Except as Except
    import Control.Monad.Trans.Reader (ReaderT(..))
    import Control.Monad.Trans.RWS.Lazy as Lazy
    import Control.Monad.Trans.RWS.Strict as Strict
    import Control.Monad.Trans.State.Lazy as Lazy
    import Control.Monad.Trans.State.Strict as Strict
    import Control.Monad.Trans.Writer.Lazy as Lazy
    import Control.Monad.Trans.Writer.Strict as Strict
    import qualified Data.Binary.Put as B
    import Data.ByteString as Strict
    import Data.ByteString.Lazy as Lazy
    import qualified Data.Serialize.Put as S
    import Data.Word

    ------------------------------------------------------------------------------
    -- MonadPut
    ------------------------------------------------------------------------------

    class (Applicative m, Monad m) => MonadPut m where
      -- | Efficiently write a byte into the output buffer
      putWord8 :: Word8 -> m ()
    #ifndef HLINT
      default putWord8 :: (m ~ t n, MonadTrans t, MonadPut n) => Word8 -> m ()
      putWord8 = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word8 -> n ()) -> Word8 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word8 -> n ()
    forall (m :: * -> *). MonadPut m => Word8 -> m ()
    putWord8
      {-# INLINE putWord8 #-}
    #endif

      -- | An efficient primitive to write a strict 'Strict.ByteString' into the output buffer.
      --
      -- In @binary@ this flushes the current buffer, and writes the argument into a new chunk.
      putByteString     :: Strict.ByteString -> m ()
    #ifndef HLINT
      default putByteString :: (m ~ t n, MonadTrans t, MonadPut n) => Strict.ByteString -> m ()
      putByteString = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (ByteString -> n ()) -> ByteString -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> n ()
    forall (m :: * -> *). MonadPut m => ByteString -> m ()
    putByteString
      {-# INLINE putByteString #-}
    #endif

      -- | Write a lazy 'Lazy.ByteString' efficiently.
      --
      -- With @binary@, this simply appends the chunks to the output buffer
      putLazyByteString :: Lazy.ByteString -> m ()
    #ifndef HLINT
      default putLazyByteString :: (m ~ t n, MonadTrans t, MonadPut n) => Lazy.ByteString -> m ()
      putLazyByteString = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (ByteString -> n ()) -> ByteString -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> n ()
    forall (m :: * -> *). MonadPut m => ByteString -> m ()
    putLazyByteString
      {-# INLINE putLazyByteString #-}
    #endif

      -- | Pop the 'ByteString' we have constructed so far, if any, yielding a
      -- new chunk in the result 'ByteString'.
      --
      -- If we're building a strict 'Strict.ByteString' with @cereal@ then this does nothing.
      flush :: m ()
    #ifndef HLINT
      default flush :: (m ~ t n, MonadTrans t, MonadPut n) => m ()
      flush = n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift n ()
    forall (m :: * -> *). MonadPut m => m ()
    flush
      {-# INLINE flush #-}
    #endif

      -- | Write a 'Word16' in little endian format
      putWord16le   :: Word16 -> m ()
    #ifndef HLINT
      default putWord16le :: (m ~ t n, MonadTrans t, MonadPut n) => Word16 -> m ()
      putWord16le = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word16 -> n ()) -> Word16 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word16 -> n ()
    forall (m :: * -> *). MonadPut m => Word16 -> m ()
    putWord16le
      {-# INLINE putWord16le #-}
    #endif

      -- | Write a 'Word16' in big endian format
      putWord16be   :: Word16 -> m ()
    #ifndef HLINT
      default putWord16be :: (m ~ t n, MonadTrans t, MonadPut n) => Word16 -> m ()
      putWord16be = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word16 -> n ()) -> Word16 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word16 -> n ()
    forall (m :: * -> *). MonadPut m => Word16 -> m ()
    putWord16be
      {-# INLINE putWord16be #-}
    #endif

      -- | /O(1)./ Write a 'Word16' in native host order and host endianness.
      -- For portability issues see 'putWordhost'.
      putWord16host :: Word16 -> m ()
    #ifndef HLINT
      default putWord16host :: (m ~ t n, MonadTrans t, MonadPut n) => Word16 -> m ()
      putWord16host = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word16 -> n ()) -> Word16 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word16 -> n ()
    forall (m :: * -> *). MonadPut m => Word16 -> m ()
    putWord16host
      {-# INLINE putWord16host #-}
    #endif

      -- | Write a 'Word32' in little endian format
      putWord32le   :: Word32 -> m ()
    #ifndef HLINT
      default putWord32le :: (m ~ t n, MonadTrans t, MonadPut n) => Word32 -> m ()
      putWord32le = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word32 -> n ()) -> Word32 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word32 -> n ()
    forall (m :: * -> *). MonadPut m => Word32 -> m ()
    putWord32le
      {-# INLINE putWord32le #-}
    #endif

      -- | Write a 'Word32' in big endian format
      putWord32be   :: Word32 -> m ()
    #ifndef HLINT
      default putWord32be :: (m ~ t n, MonadTrans t, MonadPut n) => Word32 -> m ()
      putWord32be = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word32 -> n ()) -> Word32 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word32 -> n ()
    forall (m :: * -> *). MonadPut m => Word32 -> m ()
    putWord32be
      {-# INLINE putWord32be #-}
    #endif

      -- | /O(1)./ Write a 'Word32' in native host order and host endianness.
      -- For portability issues see @putWordhost@.
      putWord32host :: Word32 -> m ()
    #ifndef HLINT
      default putWord32host :: (m ~ t n, MonadTrans t, MonadPut n) => Word32 -> m ()
      putWord32host = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word32 -> n ()) -> Word32 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word32 -> n ()
    forall (m :: * -> *). MonadPut m => Word32 -> m ()
    putWord32host
      {-# INLINE putWord32host #-}
    #endif

      -- | Write a 'Word64' in little endian format
      putWord64le   :: Word64 -> m ()
    #ifndef HLINT
      default putWord64le :: (m ~ t n, MonadTrans t, MonadPut n) => Word64 -> m ()
      putWord64le = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word64 -> n ()) -> Word64 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word64 -> n ()
    forall (m :: * -> *). MonadPut m => Word64 -> m ()
    putWord64le
      {-# INLINE putWord64le #-}
    #endif

      -- | Write a 'Word64' in big endian format
      putWord64be   :: Word64 -> m ()
    #ifndef HLINT
      default putWord64be :: (m ~ t n, MonadTrans t, MonadPut n) => Word64 -> m ()
      putWord64be = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word64 -> n ()) -> Word64 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word64 -> n ()
    forall (m :: * -> *). MonadPut m => Word64 -> m ()
    putWord64be
      {-# INLINE putWord64be #-}
    #endif

      -- | /O(1)./ Write a 'Word64' in native host order and host endianness.
      -- For portability issues see @putWordhost@.
      putWord64host :: Word64 -> m ()
    #ifndef HLINT
      default putWord64host :: (m ~ t n, MonadTrans t, MonadPut n) => Word64 -> m ()
      putWord64host = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word64 -> n ()) -> Word64 -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word64 -> n ()
    forall (m :: * -> *). MonadPut m => Word64 -> m ()
    putWord64host
      {-# INLINE putWord64host #-}
    #endif

      -- | /O(1)./ Write a single native machine word. The word is
      -- written in host order, host endian form, for the machine you're on.
      -- On a 64 bit machine the Word is an 8 byte value, on a 32 bit machine,
      -- 4 bytes. Values written this way are not portable to
      -- different endian or word sized machines, without conversion.
      putWordhost :: Word -> m ()
    #ifndef HLINT
      default putWordhost :: (m ~ t n, MonadTrans t, MonadPut n) => Word -> m ()
      putWordhost = n () -> m ()
    n () -> t n ()
    forall (m :: * -> *) a. Monad m => m a -> t m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (n () -> m ()) -> (Word -> n ()) -> Word -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Word -> n ()
    forall (m :: * -> *). MonadPut m => Word -> m ()
    putWordhost
      {-# INLINE putWordhost #-}
    #endif

    instance MonadPut B.PutM where
      putWord8 :: Word8 -> PutM ()
    putWord8 = Word8 -> PutM ()
    B.putWord8
      {-# INLINE putWord8 #-}
      putByteString :: ByteString -> PutM ()
    putByteString = ByteString -> PutM ()
    B.putByteString
      {-# INLINE putByteString #-}
      putLazyByteString :: ByteString -> PutM ()
    putLazyByteString = ByteString -> PutM ()
    B.putLazyByteString
      {-# INLINE putLazyByteString #-}
      flush :: PutM ()
    flush = PutM ()
    B.flush
      {-# INLINE flush #-}
      putWord16le :: Word16 -> PutM ()
    putWord16le   = Word16 -> PutM ()
    B.putWord16le
      {-# INLINE putWord16le #-}
      putWord16be :: Word16 -> PutM ()
    putWord16be   = Word16 -> PutM ()
    B.putWord16be
      {-# INLINE putWord16be #-}
      putWord16host :: Word16 -> PutM ()
    putWord16host = Word16 -> PutM ()
    B.putWord16host
      {-# INLINE putWord16host #-}
      putWord32le :: Word32 -> PutM ()
    putWord32le   = Word32 -> PutM ()
    B.putWord32le
      {-# INLINE putWord32le #-}
      putWord32be :: Word32 -> PutM ()
    putWord32be   = Word32 -> PutM ()
    B.putWord32be
      {-# INLINE putWord32be #-}
      putWord32host :: Word32 -> PutM ()
    putWord32host = Word32 -> PutM ()
    B.putWord32host
      {-# INLINE putWord32host #-}
      putWord64le :: Word64 -> PutM ()
    putWord64le   = Word64 -> PutM ()
    B.putWord64le
      {-# INLINE putWord64le #-}
      putWord64be :: Word64 -> PutM ()
    putWord64be   = Word64 -> PutM ()
    B.putWord64be
      {-# INLINE putWord64be #-}
      putWord64host :: Word64 -> PutM ()
    putWord64host = Word64 -> PutM ()
    B.putWord64host
      {-# INLINE putWord64host #-}
      putWordhost :: Word -> PutM ()
    putWordhost   = Word -> PutM ()
    B.putWordhost
      {-# INLINE putWordhost #-}

    instance MonadPut S.PutM where
      putWord8 :: Word8 -> PutM ()
    putWord8 = Word8 -> PutM ()
    S.putWord8
      {-# INLINE putWord8 #-}
      putByteString :: ByteString -> PutM ()
    putByteString = ByteString -> PutM ()
    S.putByteString
      {-# INLINE putByteString #-}
      putLazyByteString :: ByteString -> PutM ()
    putLazyByteString = ByteString -> PutM ()
    S.putLazyByteString
      {-# INLINE putLazyByteString #-}
      flush :: PutM ()
    flush = PutM ()
    S.flush
      {-# INLINE flush #-}
      putWord16le :: Word16 -> PutM ()
    putWord16le   = Word16 -> PutM ()
    S.putWord16le
      {-# INLINE putWord16le #-}
      putWord16be :: Word16 -> PutM ()
    putWord16be   = Word16 -> PutM ()
    S.putWord16be
      {-# INLINE putWord16be #-}
      putWord16host :: Word16 -> PutM ()
    putWord16host = Word16 -> PutM ()
    S.putWord16host
      {-# INLINE putWord16host #-}
      putWord32le :: Word32 -> PutM ()
    putWord32le   = Word32 -> PutM ()
    S.putWord32le
      {-# INLINE putWord32le #-}
      putWord32be :: Word32 -> PutM ()
    putWord32be   = Word32 -> PutM ()
    S.putWord32be
      {-# INLINE putWord32be #-}
      putWord32host :: Word32 -> PutM ()
    putWord32host = Word32 -> PutM ()
    S.putWord32host
      {-# INLINE putWord32host #-}
      putWord64le :: Word64 -> PutM ()
    putWord64le   = Word64 -> PutM ()
    S.putWord64le
      {-# INLINE putWord64le #-}
      putWord64be :: Word64 -> PutM ()
    putWord64be   = Word64 -> PutM ()
    S.putWord64be
      {-# INLINE putWord64be #-}
      putWord64host :: Word64 -> PutM ()
    putWord64host = Word64 -> PutM ()
    S.putWord64host
      {-# INLINE putWord64host #-}
      putWordhost :: Word -> PutM ()
    putWordhost   = Word -> PutM ()
    S.putWordhost
      {-# INLINE putWordhost #-}

    instance MonadPut m => MonadPut (Lazy.StateT s m)
    instance MonadPut m => MonadPut (Strict.StateT s m)
    instance MonadPut m => MonadPut (ReaderT e m)
    instance (MonadPut m, Monoid w) => MonadPut (Lazy.WriterT w m)
    instance (MonadPut m, Monoid w) => MonadPut (Strict.WriterT w m)
    instance (MonadPut m, Monoid w) => MonadPut (Lazy.RWST r w s m)
    instance (MonadPut m, Monoid w) => MonadPut (Strict.RWST r w s m)
    instance (MonadPut m) => MonadPut (ExceptT e m) where

    -- | Put a value into a lazy 'Lazy.ByteString' using 'B.runPut'.
    runPutL :: B.Put -> Lazy.ByteString
    runPutL :: PutM () -> ByteString
    runPutL = PutM () -> ByteString
    B.runPut

    -- | Put a value into a strict 'Strict.ByteString' using 'S.runPut'.
    runPutS :: S.Put -> Strict.ByteString
    runPutS :: PutM () -> ByteString
    runPutS = PutM () -> ByteString
    S.runPut