user1242770
user1242770

Reputation:

Haskell - Parse binary stream

I'm new to Haskell, so I'm probably missing something very simple.

I'm having a supprisingly hard time trying to parse a structured binary stream. The binary stream has varying and conditional parts (like a field which determines how many items follow it (ncount), or what type of message follows it (type)).

To get a simple working example, I'm trying to parse this hypothetical binary structure:

+----------------+---------------+--------------
| Magic (8 bits) | type (3 bits) | type message...
+----------------+---------------+--------------
Type 1:
+----------------+-------------+-------------+-----------------+
|ncount (3 bits) | n1 (3 bits) | n1 (3 bits) | nN (3 bits)...  |
+----------------+-------------+-------------+-----------------+
Type 2:
+----------------+---------------+
|  num1 (7 bits) | num2 (7 bits) |
+----------------+---------------+
...

My code so far:

{-# LANGUAGE RecordWildCards #-}

module Main where

import Data.Bits
import Data.Binary                      as B
import qualified Data.Binary.Bits.Get   as BG
import qualified Data.ByteString        as BS

data Header = Header {
     magic  :: Word8
    ,mtype  :: Word8
    ,num1   :: Word8
    ,num2   :: Word8
} deriving (Show)

--instance Show (Get Header) where
--    show (Header {..}) = show . magic

parse_header :: B.Get Header
parse_header = BG.runBitGet parse_header'

-- Example, assume type 2 for now
parse_header' :: BG.BitGet Header
parse_header' = do
    magic   <- BG.getWord8 8
    mtype   <- BG.getWord8 3
    num1    <- BG.getWord8 7
    num2    <- BG.getWord8 7
    return $ Header magic mtype num1 num2

main :: IO ()
main = do
    putStrLn "Start"

    -- File containing binary stream
    fstr <- BS.readFile "data/hbin.bin"

    let header = parse_header
        in 
            -- How do I print out Header?
            print header
            -- * No instance for (Show (Get Header)) 
            -- arising from a use of `print'
            -- * In the expression: print header

    putStrLn "\nEnd"

In which I get the error:

* No instance for (Show (Get Header)) arising from a use of `print'
* In the expression: print header

Obviously, I plan to parse this recursively, but for now I can't even see a value I've read.

I've followed https://wiki.haskell.org/Dealing_with_binary_data but this uses Data.Binary.Strict (binary-strict) which doesn't compile on Windows (atleast on mine).

I've also followed https://hackage.haskell.org/package/binary-bits-0.5/docs/Data-Binary-Bits-Get.html but it doesn't show how to use values you have gotten with getWord8 (Do I need to put them into an Int to read them as decimal?)

Again, I'm new to Haskell and not familiar with Monads (which I believe Get is).

Upvotes: 1

Views: 476

Answers (1)

Li-yao Xia
Li-yao Xia

Reputation: 33389

header = parse_header is only giving a new name to the parser. You need a function to run the parser, there's one here (here choosing runGet for simplicity, but you should prefer the other one, to handle the error case more easily):

runGet :: Get a -> ByteString -> a

Note that it takes a lazy ByteString (Data.ByteString.Lazy) instead of a strict one (Data.ByteString).

...
import Data.ByteString.Lazy (toLazy)

...

main = do
  fstr <- BS.readFile "data/hbin.bin"
  let header = runGet parse_header (fromStrict fstr)
  print header
  putStrLn "End"

Upvotes: 2

Related Questions