IO.binread zero byte

Hi,

I am having issues with my binary file parsing and the file pointer seems to be one byte away from the expected position.

In parse_block( {block_size, "YYY"} , assets_file_pointer) , I have one line before I return data IO.binread(1) which I believe should just advance the file pointer 1 byte. It should be reading from a point in the file that then executes parse_block( {block_size, "PPP"} , _assets_file_pointer), but I am 1 byte away from getting “PPP”… Even if I make the discard line IO.binread(2), I don’t get any closer.

parse_block( {block_size, "YYY"} , assets_file_pointer) works successfully, however I notice that for me to get the error to display in iex, I need to hit the enter key. It is like it suspends data output to the console. If I comment out the IO.binread(1), then this doesn’t happen. I suspect it is because I am reading 1 zero byte, but don’t quite understand why that messes things up. Either way, I would have still thought it would advance the file pointer by 1 byte…

Here is my complete code:

defmodule Parser do

  def parse do

    index_data = %{}
    assets_file_pointer = File.open!("data.file")

    block_meta_data = parse_block(assets_file_pointer)
    block_contents = parse_block(block_meta_data, assets_file_pointer)
    index_data = Map.merge(index_data, block_contents)

    block_meta_data = parse_block(assets_file_pointer)
    block_contents = parse_block(block_meta_data, assets_file_pointer)
    index_data = Map.merge(index_data, block_contents)

  end

  def parse_block(assets_file_pointer) do

    block_size = assets_file_pointer
    |> IO.binread(4)
    |> Helpers.reverse_binary()
    |> :binary.decode_unsigned()

    block_type = assets_file_pointer
    |> IO.binread(2)

    {block_size, block_type}

  end

  def parse_block( {block_size, "YYY"} , assets_file_pointer) do

    number_of_rooms = (block_size - 7) / 10
    |> trunc

    room_data = Enum.reduce(1..number_of_rooms, %{}, fn(_, acc) ->

      room_number = assets_file_pointer
      |> IO.binread(1)
      |> :binary.decode_unsigned

      room_name = assets_file_pointer
      |> IO.binread(9)
      |> Helpers.xor(0xFF)

      Map.put(acc, room_name, room_number)
      Map.put(acc, room_number, room_name)

    end)

    # discard end of block zero byte (not working as expected)
    IO.binread(1)

    %{"rooms" => room_data}

  end

  def parse_block( {block_size, "PPP"} , _assets_file_pointer) do

    number_of_entries = (block_size - 8) / 5
    |> trunc

    room_directory_data = Enum.reduce(1..number_of_entries, %{}, fn(_, acc) ->
      Map.put(acc, "dummy", "data")
    end)

    %{"room_directory" => room_directory_data}

  end

end

Parser.parse()

I have since noticed in the docs that there is the erlang call :file.position if you want to explicitly set the file position. Which I have now tried and it works, but there must be still something I don’t understand about how IO.binread operates…