iounpack/lib/iounpack.rb

107 lines
3.7 KiB
Ruby

require "iounpack/version"
class String
def unpack1 *a
unpack( *a).first
end unless instance_methods.include? :unpack1
end
class IOUnpack
class Directive < Struct.new( :directive, :returns, :bytelen)
def * repeat
if bytelen.respond_to? :call
bytelen[ repeat]
else
bytelen * repeat
end
end
end
directives = [
Directive.new( :C, Integer, 1),
Directive.new( :S, Integer, 2), Directive.new( :S_, Integer, 2), Directive.new( 'S!', Integer, 2),
Directive.new( :L, Integer, 4), Directive.new( :L_, Integer, 4), Directive.new( 'L!', Integer, 4),
Directive.new( :Q, Integer, 8), Directive.new( :Q_, Integer, 8), Directive.new( 'Q!', Integer, 8),
Directive.new( :c, Integer, 1),
Directive.new( :s, Integer, 2), Directive.new( :s_, Integer, 2), Directive.new( 's!', Integer, 2),
Directive.new( :l, Integer, 4), Directive.new( :l_, Integer, 4), Directive.new( 'l!', Integer, 4),
Directive.new( :q, Integer, 8), Directive.new( :q_, Integer, 8), Directive.new( 'q!', Integer, 8),
Directive.new( 'S<', Integer, 2), Directive.new( 'S!<', Integer, 2),
Directive.new( 'L<', Integer, 4), Directive.new( 'L!<', Integer, 4),
Directive.new( 'Q<', Integer, 8), Directive.new( 'Q!<', Integer, 8),
Directive.new( 's<', Integer, 2), Directive.new( 's!<', Integer, 2),
Directive.new( 'l<', Integer, 4), Directive.new( 'l!<', Integer, 4),
Directive.new( 'q<', Integer, 8), Directive.new( 'q!<', Integer, 8),
Directive.new( 'S>', Integer, 2), Directive.new( 'S!>', Integer, 2),
Directive.new( 'L>', Integer, 4), Directive.new( 'L!>', Integer, 4),
Directive.new( 'Q>', Integer, 8), Directive.new( 'Q!>', Integer, 8),
Directive.new( 's>', Integer, 2), Directive.new( 's!>', Integer, 2),
Directive.new( 'l>', Integer, 4), Directive.new( 'l!>', Integer, 4),
Directive.new( 'q>', Integer, 8), Directive.new( 'q!>', Integer, 8),
Directive.new( :n, Integer, 2), Directive.new( :N, Integer, 4),
Directive.new( :v, Integer, 2), Directive.new( :V, Integer, 4),
Directive.new( :D, Float, 8), Directive.new( :d, Float, 8),
Directive.new( :F, Float, 8), Directive.new( :f, Float, 8),
Directive.new( :E, Float, 8),
Directive.new( :e, Float, 4),
Directive.new( :G, Float, 8),
Directive.new( :g, Float, 4),
Directive.new( :A, String, 1),
Directive.new( :a, String, 1),
Directive.new( :Z, String, 1),
Directive.new( :B, String, lambda {|dir, repeat| (repeat/8.0).ceil}),
Directive.new( :b, String, lambda {|dir, repeat| (repeat/8.0).ceil}),
Directive.new( :H, String, lambda {|dir, repeat| (repeat/2.0).ceil}),
Directive.new( :h, String, lambda {|dir, repeat| (repeat/2.0).ceil}),
Directive.new( :x, nil, 1),
]
Directives = {}
DirectivePattern = '(?:'+
directives.map do |dir|
Directives[dir.directive.to_s] = dir
Regexp.quote dir.directive
end.join( '|')+')'
DirectiveRegexp = /\A\s* (?<directive> #{DirectivePattern} ) \s* (?<count> \d+)? \s*/m
def initialize pattern
@order, @len, @pattern = [], 0, pattern
loop do
case pattern
when /\A\s*\z/m then break
when DirectiveRegexp
pattern = $'
m = [Directives[$~[:directive]], $~[:count] ? $~[:count].to_i : 1]
@order.push m
@len += m.first * m.last
else
raise ArgumentError, "Unknown token: #{pattern[0]}"
end
end
end
def unpack io
io.read( @len).unpack @pattern
end
def unpack1 io
io.read( @len).unpack1 @pattern
end
end
class IO
def unpack pattern
IOUnpack.new( pattern).unpack self
end unless instance_methods.include? :unpack
def unpack1 pattern
IOUnpack.new( pattern).unpack1 self
end unless instance_methods.include? :unpack1
end