content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE Trustworthy #-}
  -----------------------------------------------------------------------------
  -- |
  -- Module      : Data.Binary
  -- Copyright   : Lennart Kolmodin
  -- License     : BSD3-style (see LICENSE)
  --
  -- Maintainer  : Lennart Kolmodin <kolmodin@gmail.com>
  -- Stability   : unstable
  -- Portability : portable to Hugs and GHC. Requires the FFI and some flexible instances.
  --
  -- Binary serialisation of Haskell values to and from lazy 'ByteString's.
  -- The Binary library provides methods for encoding Haskell values as
  -- streams of bytes directly in memory. The resulting 'ByteString' can
  -- then be written to disk, sent over the network, or further processed
  -- (for example, compressed with gzip).
  --
  -- The @binary@ package is notable in that it provides both pure, and
  -- high performance serialisation.
  --
  -- Values encoded using the 'Binary' class are always encoded in network order
  -- (big endian) form, and encoded data should be portable across
  -- machine endianness, word size, or compiler version. For example,
  -- data encoded using the 'Binary' class could be written on any machine,
  -- and read back on any another.
  --
  -- If the specifics of the data format is not important to you, for example,
  -- you are more interested in serializing and deserializing values than
  -- in which format will be used, it is possible to derive 'Binary'
  -- instances using the generic support. See 'GBinaryGet' and
  -- 'GBinaryPut'.
  --
  -- If you have specific requirements about the encoding format, you can use
  -- the encoding and decoding primitives directly, see the modules
  -- "Data.Binary.Get" and "Data.Binary.Put".
  --
  -----------------------------------------------------------------------------
  
  module Data.Binary (
  
      -- * The Binary class
        Binary(..)
      -- ** Example
      -- $example
  
      -- * Generic support
      -- $generics
      , GBinaryGet(..)
      , GBinaryPut(..)
  
      -- * The Get and Put monads
      , Get
      , Put
  
      -- * Useful helpers for writing instances
      , putWord8
      , getWord8
  
      -- * Binary serialisation
      , encode                    -- :: Binary a => a -> ByteString
      , decode                    -- :: Binary a => ByteString -> a
      , decodeOrFail
  
      -- * IO functions for serialisation
      , encodeFile                -- :: Binary a => FilePath -> a -> IO ()
      , decodeFile                -- :: Binary a => FilePath -> IO a
      , decodeFileOrFail
  
      , module Data.Word -- useful
  
      ) where
  
  import Data.Word
  
  import Data.Binary.Class
  import Data.Binary.Put
  import Data.Binary.Get
  import Data.Binary.Generic ()
  
  import qualified Data.ByteString as B ( hGet, length )
  import Data.ByteString.Lazy (ByteString)
  import qualified Data.ByteString.Lazy as L
  import qualified Data.ByteString.Lazy.Internal as L ( defaultChunkSize )
  import System.IO ( withBinaryFile, IOMode(ReadMode) )
  
  ------------------------------------------------------------------------
  
  -- $example
  -- To serialise a custom type, an instance of Binary for that type is
  -- required. For example, suppose we have a data structure:
  --
  -- > data Exp = IntE Int
  -- >          | OpE  String Exp Exp
  -- >    deriving Show
  --
  -- We can encode values of this type into bytestrings using the
  -- following instance, which proceeds by recursively breaking down the
  -- structure to serialise:
  --
  -- > instance Binary Exp where
  -- >       put (IntE i)      = do put (0 :: Word8)
  -- >                              put i
  -- >       put (OpE s e1 e2) = do put (1 :: Word8)
  -- >                              put s
  -- >                              put e1
  -- >                              put e2
  -- >
  -- >       get = do t <- get :: Get Word8
  -- >                case t of
  -- >                     0 -> do i <- get
  -- >                             return (IntE i)
  -- >                     1 -> do s  <- get
  -- >                             e1 <- get
  -- >                             e2 <- get
  -- >                             return (OpE s e1 e2)
  --
  -- Note how we write an initial tag byte to indicate each variant of the
  -- data type.
  --
  -- We can simplify the writing of 'get' instances using monadic
  -- combinators:
  --
  -- >       get = do tag <- getWord8
  -- >                case tag of
  -- >                    0 -> liftM  IntE get
  -- >                    1 -> liftM3 OpE  get get get
  --
  -- To serialise this to a bytestring, we use 'encode', which packs the
  -- data structure into a binary format, in a lazy bytestring
  --
  -- > > let e = OpE "*" (IntE 7) (OpE "/" (IntE 4) (IntE 2))
  -- > > let v = encode e
  --
  -- Where @v@ is a binary encoded data structure. To reconstruct the
  -- original data, we use 'decode'
  --
  -- > > decode v :: Exp
  -- > OpE "*" (IntE 7) (OpE "/" (IntE 4) (IntE 2))
  --
  -- The lazy ByteString that results from 'encode' can be written to
  -- disk, and read from disk using Data.ByteString.Lazy IO functions,
  -- such as hPutStr or writeFile:
  --
  -- > > writeFile "/tmp/exp.txt" (encode e)
  --
  -- And read back with:
  --
  -- > > readFile "/tmp/exp.txt" >>= return . decode :: IO Exp
  -- > OpE "*" (IntE 7) (OpE "/" (IntE 4) (IntE 2))
  --
  -- We can also directly serialise a value to and from a Handle, or a file:
  --
  -- > > v <- decodeFile  "/tmp/exp.txt" :: IO Exp
  -- > OpE "*" (IntE 7) (OpE "/" (IntE 4) (IntE 2))
  --
  -- And write a value to disk
  --
  -- > > encodeFile "/tmp/a.txt" v
  --
  
  ------------------------------------------------------------------------
  -- Wrappers to run the underlying monad
  
  -- | Encode a value using binary serialisation to a lazy ByteString.
  --
  encode :: Binary a => a -> ByteString
  encode = runPut . put
  {-# INLINE encode #-}
  
  -- | Decode a value from a lazy ByteString, reconstructing the original structure.
  decode :: Binary a => ByteString -> a
  decode = runGet get
  
  -- | Decode a value from a lazy ByteString. Returning 'Left' on failure and
  -- 'Right' on success. In both cases the unconsumed input and the number of
  -- consumed bytes is returned. In case of failure, a human-readable error
  -- message will be returned as well.
  --
  -- @since 0.7.0.0
  decodeOrFail :: Binary a => L.ByteString
               -> Either (L.ByteString, ByteOffset, String)
                         (L.ByteString, ByteOffset, a)
  decodeOrFail = runGetOrFail get
  
  
  ------------------------------------------------------------------------
  -- Convenience IO operations
  
  -- | Lazily serialise a value to a file.
  --
  -- This is just a convenience function, it's defined simply as:
  --
  -- > encodeFile f = B.writeFile f . encode
  --
  -- So for example if you wanted to compress as well, you could use:
  --
  -- > B.writeFile f . compress . encode
  --
  encodeFile :: Binary a => FilePath -> a -> IO ()
  encodeFile f v = L.writeFile f (encode v)
  
  -- | Decode a value from a file. In case of errors, 'error' will
  -- be called with the error message.
  --
  -- @since 0.7.0.0
  decodeFile :: Binary a => FilePath -> IO a
  decodeFile f = do
    result <- decodeFileOrFail f
    case result of
      Right x -> return x
      Left (_,str) -> error str
  
  -- | Decode a value from a file. In case of success, the value will be returned
  -- in 'Right'. In case of decoder errors, the error message together with
  -- the byte offset will be returned.
  decodeFileOrFail :: Binary a => FilePath -> IO (Either (ByteOffset, String) a)
  decodeFileOrFail f =
    withBinaryFile f ReadMode $ \h -> do
      feed (runGetIncremental get) h
    where -- TODO: put in Data.Binary.Get and name pushFromHandle?
      feed (Done _ _ x) _ = return (Right x)
      feed (Fail _ pos str) _ = return (Left (pos, str))
      feed (Partial k) h = do
        chunk <- B.hGet h L.defaultChunkSize
        case B.length chunk of
          0 -> feed (k Nothing) h
          _ -> feed (k (Just chunk)) h
  
  ------------------------------------------------------------------------
  -- $generics
  --
  -- Beginning with GHC 7.2, it is possible to use binary serialization
  -- without writing any instance boilerplate code.
  --
  -- > {-# LANGUAGE DeriveGeneric #-}
  -- >
  -- > import Data.Binary
  -- > import GHC.Generics (Generic)
  -- >
  -- > data Foo = Foo
  -- >          deriving (Generic)
  -- >
  -- > -- GHC will automatically fill out the instance
  -- > instance Binary Foo
  --
  -- This mechanism makes use of GHC's efficient built-in generics
  -- support.
  