Class: BinData::DelayedIO

Inherits:
Base
  • Object
show all
Extended by:
DSLMixin
Defined in:
lib/bindata/delayed_io.rb

Overview

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.

Instance Attribute Summary

Attributes inherited from Base

#parent

Instance Method Summary collapse

Methods included from DSLMixin

dsl_parser, to_ary, to_str

Methods inherited from Base

#==, #=~, arg_processor, auto_call_delayed_io, bindata_name, #clear, #debug_name, #eval_parameter, #get_parameter, #has_parameter?, #initialize_with_warning, #inspect, #lazy_evaluator, #new, #pretty_print, #read, read, register_subclasses, #safe_respond_to?, #to_binary_s, #to_hex, #to_s, unregister_self, #write

Methods included from AcceptedParametersPlugin

#accepted_parameters, #default_parameters, #mandatory_parameters, #mutually_exclusive_parameters, #optional_parameters

Methods included from RegisterNamePlugin

included, #initialize_shared_instance

Methods included from Framework

#bit_aligned?, #debug_name_of, #offset_of

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args, &block) ⇒ Object

:nodoc:



90
91
92
# File 'lib/bindata/delayed_io.rb', line 90

def method_missing(symbol, *args, &block) # :nodoc:
  @type.__send__(symbol, *args, &block)
end

Instance Method Details

#abs_offsetObject



94
95
96
# File 'lib/bindata/delayed_io.rb', line 94

def abs_offset
  @abs_offset || eval_parameter(:read_abs_offset)
end

#abs_offset=(offset) ⇒ Object

Sets the abs_offset to use when writing this object.



99
100
101
# File 'lib/bindata/delayed_io.rb', line 99

def abs_offset=(offset)
  @abs_offset = offset
end

#assign(val) ⇒ Object



74
75
76
# File 'lib/bindata/delayed_io.rb', line 74

def assign(val)
  @type.assign(val)
end

#clear?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/bindata/delayed_io.rb', line 70

def clear?
  @type.clear?
end

#do_num_bytesObject

:nodoc:



115
116
117
# File 'lib/bindata/delayed_io.rb', line 115

def do_num_bytes # :nodoc:
  0
end

#do_read(io) ⇒ Object

:nodoc:



107
108
109
# File 'lib/bindata/delayed_io.rb', line 107

def do_read(io) # :nodoc:
  @read_io = io
end

#do_write(io) ⇒ Object

:nodoc:



111
112
113
# File 'lib/bindata/delayed_io.rb', line 111

def do_write(io) # :nodoc:
  @write_io = io
end

#include_obj?Boolean

Returns:

  • (Boolean)


119
120
121
# File 'lib/bindata/delayed_io.rb', line 119

def include_obj?
  !has_parameter?(:onlyif) || eval_parameter(:onlyif)
end

#initialize_instanceObject



63
64
65
66
67
68
# 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_bytesObject



82
83
84
# File 'lib/bindata/delayed_io.rb', line 82

def num_bytes
  @type.num_bytes
end

#read_now!Object

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

Raises:

  • (IOError)


125
126
127
128
129
130
131
132
133
# 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.seek_to_abs_offset(abs_offset)
  start_read do
    @type.do_read(@read_io)
  end
end

#rel_offsetObject



103
104
105
# File 'lib/bindata/delayed_io.rb', line 103

def rel_offset
  abs_offset
end

#respond_to_missing?(symbol, include_all = false) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


86
87
88
# File 'lib/bindata/delayed_io.rb', line 86

def respond_to_missing?(symbol, include_all = false) # :nodoc:
  @type.respond_to?(symbol, include_all) || super
end

#snapshotObject



78
79
80
# File 'lib/bindata/delayed_io.rb', line 78

def snapshot
  @type.snapshot
end

#write_now!Object

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

Raises:

  • (IOError)


137
138
139
140
141
142
143
# 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.seek_to_abs_offset(abs_offset)
  @type.do_write(@write_io)
end