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* (? #{DirectivePattern} ) \s* (? \d+)? \s*/mx attr_reader :len, :pattern def initialize pattern @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] @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