# Struct does some trickery with custom allocators so we can't subclass it without writing C. # Instead we define a CStruct class that does something similar enough for our purpose. It is # subclassed just like any other class. A nice side-effect of this syntax is that it is always # clear that a CStruct is just a class and instances of the struct are objects. # # Some light metaprogramming is used to make the following syntax possible: # # class MachHeader < CStruct # uint :magic # int :cputype # int :cpusubtype # ... # int :flags # end # # Inheritance works as you would expect. # # class LoadCommand < CStruct # uint32 :cmd # uint32 :cmdsize # end # # # inherits cmd and cmdsize as the first 2 fields # class SegmentCommand < LoadCommand # string :segname, 16 # uint32 :vmaddr # uint32 # end # # Nothing tricky or confusing there. Members of a CStruct class are declared in the # class definition. A different definition using a more static approach probably wouldn't # be very hard... if performance is critical ... but then why are you using Ruby? ;-) class CStruct ################### # Class Constants # ################### # Size in bytes. SizeMap = { :int8 => 1, :uint8 => 1, :int16 => 2, :uint16 => 2, :int32 => 4, :uint32 => 4, :string => lambda { |*opts| opts.first }, # first opt is size # the last 3 are to make the language more C-like :int => 4, :uint => 4, :char => 1 } # 32-bit PackMap = { :int8 => 'c', :uint8 => 'C', :int16 => 's', :uint16 => 'S', :int32 => 'i', :uint32 => 'I', :string => lambda do |str, *opts| len = opts.first str.ljust(len, "\0")[0, len] end, # a few C-like names :int => 'i', :uint => 'I', :char => 'C' } # Only needed when unpacking is different from packing, i.e. strings w/ lambdas in PackMap. UnpackMap = { :string => lambda do |str, *opts| len = opts.first val = str[0, len-1].sub(/\0*$/, '') str.slice!((len-1)..-1) val end } ########################## # Class Instance Methods # ########################## # Note: const_get and const_set are used so the constants are bound at runtime, to the # real class that has subclassed CStruct. I figured Ruby would do this but I haven't # looked at the implementation of constants so it might be tricky. # # All of this could probably be avoided with Ruby 1.9 and private class variables. # That is definitely something to experiment with. class <