{-# 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.