|License||BSD3-style (see LICENSE)|
|Maintainer||Lennart Kolmodin <[email protected]>|
|Portability||portable to Hugs and GHC.|
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:
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. https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.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
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
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
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
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.
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.
An offset, counted in bytes.
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
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.
A decoder procuced by running a
|Fail !ByteString !ByteOffset String||
The decoder ran into an error. The decoder either used
|Partial (Maybe ByteString -> Decoder a)|
|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 myParser `pushChunk` myInput1 `pushChunk` myInput2
runGetIncremental myParser `pushChunks` myLazyByteString
n bytes. Fails if fewer than
n bytes are available.
Test whether all input has been consumed, i.e. there are no remaining undecoded bytes.
Get the total number of bytes read to this point.
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.
Run the given decoder, but without consuming its input. If the given decoder fails, then so will this function.
Label a decoder. If the decoder fails, the label will be appended on a new line to the error message string.
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.
An efficient get method for lazy ByteStrings. Fails if fewer than
n bytes are left in the input.
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.
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.
Read a Word8 from the monad state
Read a Word16 in big endian format
Read a Word32 in big endian format
Read a Word64 in big endian format
Read a Word16 in little endian format
Read a Word32 in little endian format
Read a Word64 in little endian format
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.
O(1). Read a 2 byte Word16 in native host order and host endianness.
O(1). Read a Word32 in native host order and host endianness.
O(1). Read a Word64 in native host order and host endianess.
Read an Int8 from the monad state
Read an Int16 in big endian format.
Read an Int32 in big endian format.
Read an Int64 in big endian format.
Read an Int16 in little endian format.
Read an Int32 in little endian format.
Read an Int64 in little endian format.
O(1). Read a single native machine word in native host order. It works in the same way as
O(1). Read a 2 byte Int16 in native host order and host endianness.
O(1). Read an Int32 in native host order and host endianness.
O(1). Read an Int64 in native host order and host endianess.
Float in big endian IEEE-754 format.
Float in little endian IEEE-754 format.
Float in IEEE-754 format and host endian.
Double in big endian IEEE-754 format.
Double in little endian IEEE-754 format.
Double in IEEE-754 format and host endian.
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.
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.
DEPRECATED. Same as
© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).