binary-0.7.5.0: Binary serialisation for Haskell values using lazy ByteStrings

Copyright | Lennart Kolmodin
License | BSD3-style (see LICENSE)
Maintainer | Lennart Kolmodin <kolmodin@gmail.com>
Stability | experimental
Portability | portable to Hugs and GHC.
Safe Haskell | Trustworthy
Language | Haskell98

Data.Binary.Get

Contents

- The Get monad
- The lazy input interface
- The incremental input interface
  - Providing input
- Decoding
  - ByteStrings
  - Decoding words
    - Big-endian decoding
    - Little-endian decoding
    - Host-endian, unaligned decoding
- Deprecated functions

Description

The `Get` monad. A monad for efficiently building structures from encoded lazy ByteStrings.

Primitives are available to decode words of various sizes, both big and little endian.

Let's decode binary data representing illustrated here. In this example the values are in little endian.

    +------------------+--------------+-----------------+
    | 32 bit timestamp | 32 bit price | 16 bit quantity |
    +------------------+--------------+-----------------+

A corresponding Haskell value looks like this:

    data Trade = Trade
      { timestamp :: !Word32
      , price     :: !Word32
      , qty       :: !Word16
      } deriving (Show)

The fields in `Trade` are marked as strict (using `!`) since we don't need laziness here. In practise, you would probably consider using the UNPACK pragma as well. http://www.haskell.org/ghc/docs/latest/html/users_guide/pragmas.html#unpack-pragma

Now, let's have a look at a decoder for this format.

    getTrade :: Get Trade
    getTrade = do
      timestamp <- getWord32le
      price     <- getWord32le
      quantity  <- getWord16le
      return $! Trade timestamp price quantity

Or even simpler using applicative style:

    getTrade' :: Get Trade
    getTrade' = Trade <$> getWord32le <*> getWord32le <*> getWord16le

The applicative style can sometimes result in faster code, as `binary` will try to optimize the code by grouping the reads together.

There are two kinds of ways to execute this decoder, the lazy input method and the incremental input method. Here we will use the lazy input method.

Let's first define a function that decodes many `Trade`s.

    getTrades :: Get [Trade]
    getTrades = do
      empty <- isEmpty
      if empty
        then return []
        else do trade <- getTrade
                trades <- getTrades
                return (trade:trades)

Finally, we run the decoder:

    lazyIOExample :: IO [Trade]
    lazyIOExample = do
      input <- BL.readFile "trades.bin"
      return (runGet getTrades input)

This decoder has the downside that it will need to read all the input before it can return. On the other hand, it will not return anything until it knows it could decode without any decoder errors.

You could also refactor to a left-fold, to decode in a more streaming fashion, and get the following decoder. It will start to return data without knowing that it can decode all input.

    incrementalExample :: BL.ByteString -> [Trade]
    incrementalExample input0 = go decoder input0
      where
        decoder = runGetIncremental getTrade
        go :: Decoder Trade -> BL.ByteString -> [Trade]
        go (Done leftover _consumed trade) input =
          trade : go decoder (BL.chunk leftover input)
        go (Partial k) input                     =
          go (k . takeHeadChunk $ input) (dropHeadChunk input)
        go (Fail _leftover _consumed msg) _input =
          error msg

    takeHeadChunk :: BL.ByteString -> Maybe BS.ByteString
    takeHeadChunk lbs =
      case lbs of
        (BL.Chunk bs _) -> Just bs
        _ -> Nothing

    dropHeadChunk :: BL.ByteString -> BL.ByteString
    dropHeadChunk lbs =
      case lbs of
        (BL.Chunk _ lbs') -> lbs'
        _ -> BL.Empty

The `lazyIOExample` uses lazy I/O to read the file from the disk, which is not suitable in all applications, and certainly not if you need to read from a socket which has higher likelihood to fail. To address these needs, use the incremental input method like in `incrementalExample`. For an example of how to read incrementally from a Handle, see the implementation of `decodeFileOrFail` in Data.Binary.

Synopsis

- data Get a
- runGet :: Get a -> ByteString -> a
- runGetOrFail :: Get a -> ByteString -> Either (ByteString, ByteOffset, String) (ByteString, ByteOffset, a)
- type ByteOffset = Int64
- data Decoder a
  - = Fail !ByteString !ByteOffset String
  - | Partial (Maybe ByteString -> Decoder a)
  - | Done !ByteString !ByteOffset a
- runGetIncremental :: Get a -> Decoder a
- pushChunk :: Decoder a -> ByteString -> Decoder a
- pushChunks :: Decoder a -> ByteString -> Decoder a
- pushEndOfInput :: Decoder a -> Decoder a
- skip :: Int -> Get ()
- isEmpty :: Get Bool
- bytesRead :: Get Int64
- isolate :: Int -> Get a -> Get a
- lookAhead :: Get a -> Get a
- lookAheadM :: Get (Maybe a) -> Get (Maybe a)
- lookAheadE :: Get (Either a b) -> Get (Either a b)
- label :: String -> Get a -> Get a
- getByteString :: Int -> Get ByteString
- getLazyByteString :: Int64 -> Get ByteString
- getLazyByteStringNul :: Get ByteString
- getRemainingLazyByteString :: Get ByteString
- getWord8 :: Get Word8
- getWord16be :: Get Word16
- getWord32be :: Get Word32
- getWord64be :: Get Word64
- getWord16le :: Get Word16
- getWord32le :: Get Word32
- getWord64le :: Get Word64
- getWordhost :: Get Word
- getWord16host :: Get Word16
- getWord32host :: Get Word32
- getWord64host :: Get Word64
- runGetState :: Get a -> ByteString -> ByteOffset -> (a, ByteString, ByteOffset)
- remaining :: Get Int64
- getBytes :: Int -> Get ByteString

# The Get monad

data Get a

Instances

Monad Get |
Functor Get |
Applicative Get |
Alternative Get |
MonadPlus Get |

# The lazy input interface

The lazy interface consumes a single lazy `ByteString`. It's the easiest interface to get started with, but it doesn't support interleaving I/O and parsing, unless lazy I/O is used.

There is no way to provide more input other than the initial data. To be able to incrementally give more data, see the incremental input interface.

runGet :: Get a -> ByteString -> a

The simplest interface to run a `Get` decoder. If the decoder runs into an error, calls `fail`, or runs out of input, it will call `error`.

runGetOrFail :: Get a -> ByteString -> Either (ByteString, ByteOffset, String) (ByteString, ByteOffset, a)

Run a `Get` monad and return `Left` on failure and `Right` on success. In both cases any unconsumed input and the number of bytes consumed is returned. In the case of failure, a human-readable error message is included as well.

type ByteOffset = Int64

An offset, counted in bytes.

# The incremental input interface

The incremental interface gives you more control over how input is provided during parsing. This lets you e.g. interleave parsing and I/O.

The incremental interface consumes a strict `ByteString` at a time, each being part of the total amount of input. If your decoder needs more input to finish it will return a `Partial` with a continuation. If there is no more input, provide it `Nothing`.

`Fail` will be returned if it runs into an error, together with a message, the position and the remaining input. If it succeeds it will return `Done` with the resulting value, the position and the remaining input.

data Decoder a

A decoder procuced by running a `Get` monad.

Constructors

Fail !ByteString !ByteOffset String | The decoder ran into an error. The decoder either used `fail` or was not provided enough input. Contains any unconsumed input and the number of bytes consumed.
Partial (Maybe ByteString -> Decoder a) | The decoder has consumed the available input and needs more to continue. Provide `Just` if more input is available and `Nothing` otherwise, and you will get a new `Decoder`.
Done !ByteString !ByteOffset a | The decoder has successfully finished. Except for the output value you also get any unused input as well as the number of bytes consumed.

runGetIncremental :: Get a -> Decoder a

Run a `Get` monad. See `Decoder` for what to do next, like providing input, handling decoder errors and to get the output value. Hint: Use the helper functions `pushChunk`, `pushChunks` and `pushEndOfInput`.

## Providing input

pushChunk :: Decoder a -> ByteString -> Decoder a

Feed a `Decoder` with more input. If the `Decoder` is `Done` or `Fail` it will add the input to `ByteString` of unconsumed input.

       runGetIncremental myParser `pushChunk` myInput1 `pushChunk` myInput2

pushChunks :: Decoder a -> ByteString -> Decoder a

Feed a `Decoder` with more input. If the `Decoder` is `Done` or `Fail` it will add the input to `ByteString` of unconsumed input.

       runGetIncremental myParser `pushChunks` myLazyByteString

pushEndOfInput :: Decoder a -> Decoder a

Tell a `Decoder` that there is no more input. This passes `Nothing` to a `Partial` decoder, otherwise returns the decoder unchanged.

# Decoding

skip :: Int -> Get ()

Skip ahead `n` bytes. Fails if fewer than `n` bytes are available.

isEmpty :: Get Bool

Test whether all input has been consumed, i.e. there are no remaining undecoded bytes.

bytesRead :: Get Int64

Get the total number of bytes read to this point.

isolate

Arguments

:: Int | The number of bytes that must be consumed
-> Get a | The decoder to isolate
-> Get a |

Isolate a decoder to operate with a fixed number of bytes, and fail if fewer bytes were consumed, or more bytes were attempted to be consumed. If the given decoder fails, `isolate` will also fail. Offset from `bytesRead` will be relative to the start of `isolate`, not the absolute of the input.

lookAhead :: Get a -> Get a

Run the given decoder, but without consuming its input. If the given decoder fails, then so will this function.

lookAheadM :: Get (Maybe a) -> Get (Maybe a)

Run the given decoder, and only consume its input if it returns `Just`. If `Nothing` is returned, the input will be unconsumed. If the given decoder fails, then so will this function.

lookAheadE :: Get (Either a b) -> Get (Either a b)

Run the given decoder, and only consume its input if it returns `Right`. If `Left` is returned, the input will be unconsumed. If the given decoder fails, then so will this function.

label :: String -> Get a -> Get a

## ByteStrings

getByteString :: Int -> Get ByteString

An efficient get method for strict ByteStrings. Fails if fewer than `n` bytes are left in the input. If `n <= 0` then the empty string is returned.

getLazyByteString :: Int64 -> Get ByteString

An efficient get method for lazy ByteStrings. Fails if fewer than `n` bytes are left in the input.

getLazyByteStringNul :: Get ByteString

Get a lazy ByteString that is terminated with a NUL byte. The returned string does not contain the NUL byte. Fails if it reaches the end of input without finding a NUL.

getRemainingLazyByteString :: Get ByteString

Get the remaining bytes as a lazy ByteString. Note that this can be an expensive function to use as it forces reading all input and keeping the string in-memory.

## Decoding words

getWord8 :: Get Word8

Read a Word8 from the monad state

### Big-endian decoding

getWord16be :: Get Word16

Read a Word16 in big endian format

getWord32be :: Get Word32

Read a Word32 in big endian format

getWord64be :: Get Word64

Read a Word64 in big endian format

### Little-endian decoding

getWord16le :: Get Word16

Read a Word16 in little endian format

getWord32le :: Get Word32

Read a Word32 in little endian format

getWord64le :: Get Word64

Read a Word64 in little endian format

### Host-endian, unaligned decoding

getWordhost :: Get Word

O(1). Read a single native machine word. The word is read 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.

getWord16host :: Get Word16

O(1). Read a 2 byte Word16 in native host order and host endianness.

getWord32host :: Get Word32

O(1). Read a Word32 in native host order and host endianness.

getWord64host :: Get Word64

O(1). Read a Word64 in native host order and host endianess.

# Deprecated functions

runGetState :: Get a -> ByteString -> ByteOffset -> (a, ByteString, ByteOffset)

Deprecated: Use runGetIncremental instead. This function will be removed.

DEPRECATED. Provides compatibility with previous versions of this library. Run a `Get` monad and return a tuple with three values. The first value is the result of the decoder. The second and third are the unused input, and the number of consumed bytes.

remaining :: Get Int64

Deprecated: This will force all remaining input, don't use it.

DEPRECATED. Get the number of bytes of remaining input. Note that this is an expensive function to use as in order to calculate how much input remains, all input has to be read and kept in-memory. The decoder keeps the input as a strict bytestring, so you are likely better off by calculating the remaining input in another way.

getBytes :: Int -> Get ByteString

Deprecated: Use `getByteString` instead of `getBytes`.

DEPRECATED. Same as `getByteString`.