module BinData
# == Parameters
#
# Parameters may be provided at initialisation to control the behaviour of
# an object. These parameters are:
#
# [:check_offset] Raise an error if the current IO offset doesn't
# meet this criteria. A boolean return indicates
# success or failure. Any other return is compared
# to the current offset. The variable +offset+
# is made available to any lambda assigned to
# this parameter. This parameter is only checked
# before reading.
# [:adjust_offset] Ensures that the current IO offset is at this
# position before reading. This is like
# :check_offset, except that it will
# adjust the IO offset instead of raising an error.
module CheckOrAdjustOffsetMixin
def self.included(base) #:nodoc:
base.optional_parameters :check_offset, :adjust_offset
base.mutually_exclusive_parameters :check_offset, :adjust_offset
end
# Ideally these two methods should be protected,
# but Ruby 1.9.2 requires them to be public.
# see http://redmine.ruby-lang.org/issues/show/2375
def do_read_with_check_offset(io) #:nodoc:
check_offset(io)
do_read_without_check_offset(io)
end
def do_read_with_adjust_offset(io) #:nodoc:
adjust_offset(io)
do_read_without_adjust_offset(io)
end
#---------------
private
# To be called from BinData::Base#initialize.
#
# Monkey patches #do_read to check or adjust the stream offset
# as appropriate.
def add_methods_for_check_or_adjust_offset
if has_parameter?(:check_offset)
class << self
alias_method :do_read_without_check_offset, :do_read
alias_method :do_read, :do_read_with_check_offset
end
end
if has_parameter?(:adjust_offset)
class << self
alias_method :do_read_without_adjust_offset, :do_read
alias_method :do_read, :do_read_with_adjust_offset
end
end
end
def check_offset(io)
actual_offset = io.offset
expected = eval_parameter(:check_offset, :offset => actual_offset)
if not expected
raise ValidityError, "offset not as expected for #{debug_name}"
elsif actual_offset != expected and expected != true
raise ValidityError,
"offset is '#{actual_offset}' but " +
"expected '#{expected}' for #{debug_name}"
end
end
def adjust_offset(io)
actual_offset = io.offset
expected = eval_parameter(:adjust_offset)
if actual_offset != expected
begin
seek = expected - actual_offset
io.seekbytes(seek)
warn "adjusting stream position by #{seek} bytes" if $VERBOSE
rescue
raise ValidityError,
"offset is '#{actual_offset}' but couldn't seek to " +
"expected '#{expected}' for #{debug_name}"
end
end
end
end
end