106 lines
3.7 KiB
Ruby
106 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
|