class BinData::DelayedIO

BinData declarations are evaluated in a single pass. However, some binary formats require multi pass processing. A common reason is seeking backwards in the input stream.

DelayedIO supports multi pass processing. It works by ignoring the normal read or write calls. The user must explicitly call the read_now! or write_now! methods to process an additional pass. This additional pass must specify the abs_offset of the I/O operation.

require 'bindata'

obj = BinData::DelayedIO.new(read_abs_offset: 3, type: :uint16be)
obj.read("\x00\x00\x00\x11\x12")
obj #=> 0

obj.read_now!
obj #=> 0x1112

- OR -

obj.read("\x00\x00\x00\x11\x12") { obj.read_now! } #=> 0x1122

obj.to_binary_s { obj.write_now! } #=> "\x00\x00\x00\x11\x12"

You can use the auto_call_delayed_io keyword to cause read and write to automatically perform the extra passes.

class ReversePascalString < BinData::Record
  auto_call_delayed_io

  delayed_io :str, read_abs_offset: 0 do
    string read_length: :len
  end
  count_bytes_remaining :total_size
  skip to_abs_offset: -> { total_size - 1 }
  uint8  :len, value: -> { str.length }
end

s = ReversePascalString.read("hello\x05")
s.to_binary_s #=> "hello\x05"

Parameters

Parameters may be provided at initialisation to control the behaviour of an object. These params are:

:read_abs_offset

The abs_offset to start reading at.

:type

The single type inside the delayed io. Use a struct if multiple fields are required.

Public Instance Methods

abs_offset() click to toggle source
# File lib/bindata/delayed_io.rb, line 94
def abs_offset
  @abs_offset || eval_parameter(:read_abs_offset)
end
abs_offset=(offset) click to toggle source

Sets the abs_offset to use when writing this object.

# File lib/bindata/delayed_io.rb, line 99
def abs_offset=(offset)
  @abs_offset = offset
end
assign(val) click to toggle source
# File lib/bindata/delayed_io.rb, line 74
def assign(val)
  @type.assign(val)
end
clear?() click to toggle source
# File lib/bindata/delayed_io.rb, line 70
def clear?
  @type.clear?
end
include_obj?() click to toggle source
# File lib/bindata/delayed_io.rb, line 119
def include_obj?
  ! has_parameter?(:onlyif) || eval_parameter(:onlyif)
end
initialize_instance() click to toggle source
# File lib/bindata/delayed_io.rb, line 63
def initialize_instance
  @type       = get_parameter(:type).instantiate(nil, self)
  @abs_offset = nil
  @read_io    = nil
  @write_io   = nil
end
num_bytes() click to toggle source
# File lib/bindata/delayed_io.rb, line 82
def num_bytes
  @type.num_bytes
end
read_now!() click to toggle source

DelayedIO objects aren’t read when read is called. The reading is delayed until this method is called.

# File lib/bindata/delayed_io.rb, line 125
def read_now!
  return unless include_obj?
  raise IOError, "read from where?" unless @read_io

  @read_io.seekbytes(abs_offset - @read_io.offset)
  start_read do
    @type.do_read(@read_io)
  end
end
rel_offset() click to toggle source
# File lib/bindata/delayed_io.rb, line 103
def rel_offset
  abs_offset
end
snapshot() click to toggle source
# File lib/bindata/delayed_io.rb, line 78
def snapshot
  @type.snapshot
end
write_now!() click to toggle source

DelayedIO objects aren’t written when write is called. The writing is delayed until this method is called.

# File lib/bindata/delayed_io.rb, line 137
def write_now!
  return unless include_obj?
  raise IOError, "write to where?" unless @write_io

  @write_io.seekbytes(abs_offset - @write_io.offset)
  @type.do_write(@write_io)
end