{-# LANGUAGE CPP #-} {-# LANGUAGE Trustworthy #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Binary -- Copyright : Lennart Kolmodin -- License : BSD3-style (see LICENSE) -- -- Maintainer : Lennart Kolmodin -- 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.