{-# 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 :: forall a. Binary a => a -> ByteString
encode = Put -> ByteString
runPut (Put -> ByteString) -> (a -> Put) -> a -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Put
forall t. Binary t => t -> Put
put
{-# INLINE encode #-}

-- | Decode a value from a lazy ByteString, reconstructing the original structure.
decode :: Binary a => ByteString -> a
decode :: forall a. Binary a => ByteString -> a
decode = Get a -> ByteString -> a
forall a. Get a -> ByteString -> a
runGet Get a
forall t. Binary t => Get t
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 :: forall a.
Binary a =>
ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, a)
decodeOrFail = Get a
-> ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, a)
forall a.
Get a
-> ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, a)
runGetOrFail Get a
forall t. Binary t => Get t
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 :: forall a. Binary a => String -> a -> IO ()
encodeFile String
f a
v = String -> ByteString -> IO ()
L.writeFile String
f (a -> ByteString
forall a. Binary a => a -> ByteString
encode a
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 :: forall a. Binary a => String -> IO a
decodeFile String
f = do
  Either (ByteOffset, String) a
result <- String -> IO (Either (ByteOffset, String) a)
forall a. Binary a => String -> IO (Either (ByteOffset, String) a)
decodeFileOrFail String
f
  case Either (ByteOffset, String) a
result of
    Right a
x -> a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x
    Left (ByteOffset
_,String
str) -> String -> IO a
forall a. HasCallStack => String -> a
error String
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 :: forall a. Binary a => String -> IO (Either (ByteOffset, String) a)
decodeFileOrFail String
f =
  String
-> IOMode
-> (Handle -> IO (Either (ByteOffset, String) a))
-> IO (Either (ByteOffset, String) a)
forall r. String -> IOMode -> (Handle -> IO r) -> IO r
withBinaryFile String
f IOMode
ReadMode ((Handle -> IO (Either (ByteOffset, String) a))
 -> IO (Either (ByteOffset, String) a))
-> (Handle -> IO (Either (ByteOffset, String) a))
-> IO (Either (ByteOffset, String) a)
forall a b. (a -> b) -> a -> b
$ \Handle
h -> do
    Decoder a -> Handle -> IO (Either (ByteOffset, String) a)
forall {b}.
Decoder b -> Handle -> IO (Either (ByteOffset, String) b)
feed (Get a -> Decoder a
forall a. Get a -> Decoder a
runGetIncremental Get a
forall t. Binary t => Get t
get) Handle
h
  where -- TODO: put in Data.Binary.Get and name pushFromHandle?
    feed :: Decoder b -> Handle -> IO (Either (ByteOffset, String) b)
feed (Done ByteString
_ ByteOffset
_ b
x) Handle
_ = Either (ByteOffset, String) b -> IO (Either (ByteOffset, String) b)
forall (m :: * -> *) a. Monad m => a -> m a
return (b -> Either (ByteOffset, String) b
forall a b. b -> Either a b
Right b
x)
    feed (Fail ByteString
_ ByteOffset
pos String
str) Handle
_ = Either (ByteOffset, String) b -> IO (Either (ByteOffset, String) b)
forall (m :: * -> *) a. Monad m => a -> m a
return ((ByteOffset, String) -> Either (ByteOffset, String) b
forall a b. a -> Either a b
Left (ByteOffset
pos, String
str))
    feed (Partial Maybe ByteString -> Decoder b
k) Handle
h = do
      ByteString
chunk <- Handle -> Int -> IO ByteString
B.hGet Handle
h Int
L.defaultChunkSize
      case ByteString -> Int
B.length ByteString
chunk of
        Int
0 -> Decoder b -> Handle -> IO (Either (ByteOffset, String) b)
feed (Maybe ByteString -> Decoder b
k Maybe ByteString
forall a. Maybe a
Nothing) Handle
h
        Int
_ -> Decoder b -> Handle -> IO (Either (ByteOffset, String) b)
feed (Maybe ByteString -> Decoder b
k (ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
chunk)) Handle
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.