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
# File lib/bindata/delayed_io.rb, line 94 def abs_offset @abs_offset || eval_parameter(:read_abs_offset) end
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
# File lib/bindata/delayed_io.rb, line 74 def assign(val) @type.assign(val) end
# File lib/bindata/delayed_io.rb, line 70 def clear? @type.clear? end
# File lib/bindata/delayed_io.rb, line 119 def include_obj? ! has_parameter?(:onlyif) || eval_parameter(:onlyif) end
# 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
# File lib/bindata/delayed_io.rb, line 82 def num_bytes @type.num_bytes end
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
# File lib/bindata/delayed_io.rb, line 103 def rel_offset abs_offset end
# File lib/bindata/delayed_io.rb, line 78 def snapshot @type.snapshot end
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