CStruct is a simulation of the C language's struct. Its main purpose is to manipulate binary-data conveniently in Ruby.
signed types: char,int8,int16,int32,int64,float,double
unsigned types: uchar,uint8,uint16,uint32,uint64
source: github.com/skandhas/cstruct
document: cstruct.rubyforge.org/documents.html
examples: cstruct.rubyforge.org/examples.html
version
Return the align of a struct;the default is 1.
# File lib/cstruct/cstruct.rb, line 31 def align @options[:align] end
Return the endian of a struct;the default is :little.
# File lib/cstruct/cstruct.rb, line 26 def endian @options[:endian] end
Return the fields of a struct.
# File lib/cstruct/cstruct.rb, line 36 def fields @fields end
set the options of a struct.
# File lib/cstruct/cstruct.rb, line 21 def options opts @options.merge! opts end
Return the size of a struct;size similar to
sizeof in C language.
Example:
class Point < CStruct int32:x int32:y end puts Point.size # or Point.__size__
# File lib/cstruct/cstruct.rb, line 50 def size @options[:layout_size] end
Define a anonymous struct field.
Example:
in C:
struct Window
{
int style;
struct{
int x;
int y;
}position;
};
use CStruct in Ruby:
class Window < CStruct int32:style struct :position do int32:x int32:y end end
# File lib/cstruct/cstruct.rb, line 451 def CStruct.struct symbol,&block struct_super = self.ancestors[1] struct_class = Class.new(struct_super) struct_class.instance_eval(&block) structfield symbol,struct_class,struct_class.__size__ end
Define a anonymous union field.
Example: in C:
struct U
{
union{
int x;
int y;
}value; values is anonymous union's instance
};
use CStruct in Ruby:
class U < CStruct union:value do int32:x int32:y end end
# File lib/cstruct/cstruct.rb, line 405 def CStruct.union symbol,&block union_super = self.ancestors[1] union_class = Class.new(union_super) do def self.change_to_union #:nodoc: @fields.each_key { |symbol| @fields[symbol].offset = 0 } max_field_size = @fields.values.inject(0)do |max,field| dimension = field.dimension dimension_product = 1 if dimension.is_a? Array dimension_product = dimension.inject(1){|m,d| m *= d } end field_size = field.size* dimension_product max = (field_size> max ? field_size : max) end @options[:layout_size] = max_field_size end end union_class.instance_eval(&block) union_class.instance_eval{change_to_union} structfield symbol,union_class,union_class.size end
# File lib/cstruct/cstruct.rb, line 143 def self.do_arrayfield(field) symbol = field.tag define_method(symbol) { array_field_to_value(symbol) } define_method("#{symbol}=") { |value| value_to_arrayfield(symbol,value) } end
# File lib/cstruct/cstruct.rb, line 155 def self.do_arraystructfield(field,sclass) symbol = field.tag define_method(symbol) { array_struct_field_to_value(symbol,sclass) } define_method("#{symbol}=") { |value| value_to_array_struct_field(symbol,sclass,value) } end
# File lib/cstruct/cstruct.rb, line 137 def self.do_field(field) symbol = field.tag define_method(symbol) { normal_field_to_value(symbol) } define_method("#{symbol}=") { |value| value_to_field(symbol,value) } end
# File lib/cstruct/cstruct.rb, line 149 def self.do_structfield(field,sclass) symbol = field.tag define_method(symbol) { struct_field_to_value(symbol,sclass)} define_method("#{symbol}=") { |value| value_to_struct_field(symbol,sclass,value)} end
# File lib/cstruct/cstruct.rb, line 63 def self.field(symbol,fsize,fsign,dimension = nil) field = Field.new(symbol,fsize,@options[:layout_size],fsign,dimension) @options[:layout_size] += field.byte_size @fields[symbol] = field dimension.is_a?(Array) ? (do_arrayfield field):(do_field field) end
# File lib/cstruct/cstruct.rb, line 77 def self.inherited(clazz) clazz.init_class_variables end
# File lib/cstruct/cstruct.rb, line 58 def self.init_class_variables @fields = {} @options= { :layout_size =>0, :endian => :little, :align => 1} end
# File lib/cstruct/cstruct.rb, line 81 def self.method_missing(method, *args) sclass = method_to_class_by_class self,method sclass = method_to_class_by_class Object,method unless sclass sclass = method_to_class_by_namespace method unless sclass sclass = method_to_class method unless sclass super unless sclass is_cstruct = sclass.ancestors.select{|x| x == CStruct } if is_cstruct symbol,dimension = *args structfield symbol,sclass,sclass.size,dimension else super end end
# File lib/cstruct/cstruct.rb, line 96 def self.method_to_class_by_class(clazz,method) method_string_or_symbol = RUBY_VERSION < '1.9' ? (method.to_s):(method) unless clazz.constants.include? method_string_or_symbol return nil end clazz.const_get method end
# File lib/cstruct/cstruct.rb, line 165 def self.signed(symbol,fsize,dimension) field symbol,fsize,:signed,dimension end
# File lib/cstruct/cstruct.rb, line 70 def self.structfield(symbol,sclass,ssize,dimension = nil) field = Field.new(symbol,ssize,@options[:layout_size],:struct,dimension) @options[:layout_size] += field.byte_size @fields[symbol] = field dimension.is_a?(Array) ? (do_arraystructfield field,sclass):(do_structfield field,sclass) end
# File lib/cstruct/cstruct.rb, line 161 def self.unsigned(symbol,fsize,dimension) field symbol,fsize,:unsigned,dimension end
Assign to CStruct's instance.
# File lib/cstruct/cstruct.rb, line 229 def << bindata count = @data.size < bindata.size ? @data.size : bindata.size (0...count).each do |i| Utils.string_setbyte @data,i,Utils.string_getbyte(bindata,i) end end
Return the data buffer of a CStruct's instance.
# File lib/cstruct/cstruct.rb, line 212 def data @data end
Assign to CStruct's instance.
# File lib/cstruct/cstruct.rb, line 223 def data= bindata raise 'Data Type Error!' unless bindata.is_a? String self << bindata end
Fill the date buffer of a CStruct's instance with zero.
# File lib/cstruct/cstruct.rb, line 217 def reset (0...self.class.size).each { |i| Utils.string_setbyte @data,i, 0 } sync_to_owner end
# File lib/cstruct/cstruct.rb, line 255 def array_field_to_value(symbol) make_array_normal_field(self.class.fields[symbol],self.class.endian) end
# File lib/cstruct/cstruct.rb, line 263 def array_struct_field_to_value(symbol,sclass) make_array_struct_field(self.class.fields[symbol],self.class.endian,sclass) end
# File lib/cstruct/cstruct.rb, line 365 def buffer_to_single_struct(sclass,ssize,soffset,iterator,sendian,fsign) value = sclass.new value << @data[soffset + iterator * ssize,ssize] value.owner << [@data,soffset + iterator * ssize,ssize] unless self.owner.empty? value.owner += self.owner end value end
# File lib/cstruct/cstruct.rb, line 355 def buffer_to_single_value(fsize,foffset,iterator,sendian,fsign) CStruct::Utils::unpack(@data[foffset + iterator * fsize,fsize],sendian,fsize,fsign) end
# File lib/cstruct/cstruct.rb, line 377 def buffer_to_structs(field,sendian,sclass) (0...field.byte_size/field.size).map do |i| buffer_to_single_struct(sclass,field.size,field.offset,i,sendian,field.sign) end end
# File lib/cstruct/cstruct.rb, line 359 def buffer_to_values(field,sendian) (0...field.byte_size/field.size).map do |i| buffer_to_single_value(field.size,field.offset,i,sendian,field.sign) end end
# File lib/cstruct/cstruct.rb, line 318 def make_array_normal_field(field,sendian,sclass = nil) dataref= @data objref = self value = buffer_to_values field,sendian def value.metaclass class<<self; self; end end value.metaclass.__send__ :define_method,:[]= do |i,v| bin_string = Utils::pack [v],sendian,field.size,field.sign Utils.buffer_setbytes dataref,bin_string,field.offset + i * field.size objref.sync_to_owner end case field.size when 1 value.metaclass.__send__(:define_method,:to_cstr){ value.pack("C#{value.index(0)}") } when 2 if RUBY_VERSION > "1.9" utf_endian = {:little=>"LE",:big=>"BE"} value.metaclass.__send__ :define_method,:to_wstr do value.pack("S#{value.index(0)}").force_encoding("UTF-16#{utf_endian[sendian]}") end end when 4 value.metaclass.__send__ :define_method,:to_wstr do value.pack("L#{value.index(0)}").force_encoding("UTF-32#{utf_endian[sendian]}") end end value end
# File lib/cstruct/cstruct.rb, line 351 def make_array_struct_field(field,sendian,sclass) buffer_to_structs field,sendian,sclass end
# File lib/cstruct/cstruct.rb, line 304 def make_normal_field(field,sendian,sclass = nil) Utils::unpack(@data[field.offset,field.size],sendian,field.size,field.sign) end
# File lib/cstruct/cstruct.rb, line 308 def make_struct_field(field,sendian,sclass) value = sclass.new value << @data[field.offset,field.size] value.owner << [@data,field.offset,field.size] unless self.owner.empty? value.owner += self.owner end value end
# File lib/cstruct/cstruct.rb, line 251 def normal_field_to_value(symbol) make_normal_field(self.class.fields[symbol],self.class.endian) end
# File lib/cstruct/cstruct.rb, line 259 def struct_field_to_value(symbol,sclass) make_struct_field(self.class.fields[symbol],self.class.endian,sclass) end
# File lib/cstruct/cstruct.rb, line 300 def value_to_array_struct_field(symbol,sclass,value) raise "No Implement!(CStruct,version:#{CStruct::VERSION})" end
# File lib/cstruct/cstruct.rb, line 279 def value_to_arrayfield(symbol,value) field = self.class.fields[symbol] array_length = field.byte_size/field.size if field.size == 1 and value.is_a? String if value.length >= array_length puts "WARNING: \"#{value}\".length(#{value.length}) >= #{symbol}.length(#{dimension})!!" end value = value[0...array_length-1] CStruct::Utils.buffer_setbytes @data,value,field.offset sync_to_owner else raise "No Implement!(CStruct,version:#{CStruct::VERSION})" end end
# File lib/cstruct/cstruct.rb, line 267 def value_to_field(symbol,value) dataref = @data onwerref = @owner self.class.class_eval do field = @fields[symbol] bin_string = CStruct::Utils::pack [value],@options[:endian],field.byte_size,field.sign CStruct::Utils.buffer_setbytes dataref,bin_string,field.offset end sync_to_owner end
# File lib/cstruct/cstruct.rb, line 296 def value_to_struct_field(symbol,sclass,value) raise "No Implement!(CStruct,version:#{CStruct::VERSION})" end