class FFI::StructEx
Constants
- NUMBER_TYPES
- SIGNED_NUMBER_TYPES
- UNSIGNED_NUMBER_TYPES
Attributes
bits_offset[RW]
bits_size[RW]
field_specs[R]
struct_class[RW]
type[RW]
Public Class Methods
alignment()
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 34 def alignment; struct_class.alignment; end
bit_field(type, bits_size, bits_offset)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 56 def bit_field(type, bits_size, bits_offset) Class.new(FFI::StructLayout::Field) do class << self attr_accessor :type, :bits_size, :bits_offset, :struct_class #no need to implement alignment b/c we always provide offset when adding this field to struct_layout_builder end self.struct_class = Class.new(Struct) do layout('', type) end self.type, self.bits_size, self.bits_offset = type, bits_size, bits_offset def initialize(name, offset, type) super(name, offset, FFI::Type::Struct.new(self.class.struct_class)) end def read(ptr) ptr.slice(offset, size).send("read_uint#{size * 8}".to_sym) end def write(ptr, value) ptr.slice(offset, size).send("write_uint#{size * 8}".to_sym, value) end def get(ptr) mask = (1 << self.class.bits_size) - 1 value = (read(ptr) >> self.class.bits_offset) & mask SIGNED_NUMBER_TYPES.include?(self.class.type) ? value.to_signed(self.class.bits_size) : value end def put(ptr, value) mask = ((1 << self.class.bits_size) - 1) << self.class.bits_offset write(ptr, (read(ptr) & ~mask) | ((value << self.class.bits_offset) & mask)) end end end
get(ptr)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 46 def get(ptr) type.struct_class.new(ptr.slice(offset, size)) end
initialize(name, offset, type)
click to toggle source
Calls superclass method
# File lib/ffi/struct_ex/struct_ex.rb, line 42 def initialize(name, offset, type) super(name, offset, FFI::Type::Struct.new(self.class.struct_class)) end
new(options = {})
click to toggle source
Calls superclass method
# File lib/ffi/struct_ex/struct_ex.rb, line 163 def initialize(options = {}) if options.is_a?(FFI::Pointer) super(options) else super() write(options) end end
put(ptr, value)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 50 def put(ptr, value) type.struct_class.new(ptr.slice(offset, size)).write(value) end
read(ptr)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 73 def read(ptr) ptr.slice(offset, size).send("read_uint#{size * 8}".to_sym) end
size()
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 35 def size; struct_class.size; end
struct_ex(*field_specs)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 29 def struct_ex(*field_specs) Class.new(FFI::StructLayout::Field) do class << self attr_accessor :struct_class def alignment; struct_class.alignment; end def size; struct_class.size; end end self.struct_class = Class.new(StructEx) do layout(*field_specs) end def initialize(name, offset, type) super(name, offset, FFI::Type::Struct.new(self.class.struct_class)) end def get(ptr) type.struct_class.new(ptr.slice(offset, size)) end def put(ptr, value) type.struct_class.new(ptr.slice(offset, size)).write(value) end end end
write(ptr, value)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 77 def write(ptr, value) ptr.slice(offset, size).send("write_uint#{size * 8}".to_sym, value) end
Private Class Methods
array_layout(builder, field_specs)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 98 def array_layout(builder, field_specs) field_spec_class = ::Struct.new(:name, :type, :descriptors) @field_specs = {} bits_unit = nil i = 0 while i < field_specs.size name, type = field_specs[i, 2] i += 2 unless type.is_a?(Integer) || type.is_a?(String) # If the next param is a Integer, it specifies the offset if field_specs[i].is_a?(Integer) offset = field_specs[i] i += 1 else offset = nil end type = find_field_type(type) builder.add name, type, offset bits_unit = nil else if type.is_a?(Integer) ffi_type, bits_size = UNSIGNED_NUMBER_TYPES.find {|ffi_type| type <= ffi_type.size * 8}, type raise "Unrecognized format #{type}" unless ffi_type elsif type.is_a?(String) m = /^(?<ffi_type>[\w_]+)\s*:\s*(?<bits_size>\d+)$/.match(type.strip) raise "Unrecognized format #{type}" unless m ffi_type, bits_size = find_field_type(m[:ffi_type].to_sym), m[:bits_size].to_i raise "Unrecognized type #{type}" unless NUMBER_TYPES.include?(ffi_type) end raise "Illegal format #{type}" if bits_size > ffi_type.size * 8 # Adjacent bit fields are packed into the same 1-, 2-, or 4-byte allocation unit if the integral types are the same size # and if the next bit field fits into the current allocation unit without crossing the boundary # imposed by the common alignment requirements of the bit fields. if bits_unit && bits_unit[:ffi_type].size == ffi_type.size && bits_unit[:bits_size] + bits_size <= bits_unit[:ffi_type].size * 8 bits_unit[:bits_size] += bits_size else offset = builder.send(:align, builder.size, @packed ? [@packed, ffi_type.alignment].min : [@min_alignment || 1, ffi_type.alignment].max) bits_unit = {ffi_type: ffi_type, bits_size: bits_size} end builder.add name, find_field_type(bit_field(ffi_type, bits_size, bits_unit[:bits_size] - bits_size)), offset end if field_specs[i].is_a?(Hash) descriptors = field_specs[i] i += 1 else descriptors = {} end @field_specs[name] = field_spec_class.new(name, type, descriptors) end end
Public Instance Methods
==(other)
click to toggle source
Calls superclass method
# File lib/ffi/struct_ex/struct_ex.rb, line 187 def ==(other) if other.is_a?(Hash) other.all? {|k, v| self[k] == self.map_field_value(k, v)} else super end end
[]=(field_name, value)
click to toggle source
Set field value
Calls superclass method
# File lib/ffi/struct_ex/struct_ex.rb, line 173 def []=(field_name, value) super(field_name, map_field_value(field_name, value)) end
map_field_value(field_name, value)
click to toggle source
Return mapped field value by converting {value} to corresponding native form. The priority is
1. look for descriptors 2. simple conversion from string to integer if integer type 3. {value} itself
@param [String, Symbol] field_name name of the field @param [String, Integer
, Object] value value in descriptive form or native form @return [Object] value in native form
# File lib/ffi/struct_ex/struct_ex.rb, line 204 def map_field_value(field_name, value) field_spec = self.class.field_specs[field_name] descriptor_key = value.kind_of?(String) ? value.downcase : value return field_spec.descriptors[descriptor_key] if field_spec.descriptors.has_key?(descriptor_key) type = field_spec.type return value.to_dec if (type.is_a?(Integer) || type.is_a?(String) || FFI::StructLayoutBuilder::NUMBER_TYPES.include?(type)) && value.is_a?(String) value end
write(value)
click to toggle source
# File lib/ffi/struct_ex/struct_ex.rb, line 177 def write(value) if value.is_a?(Hash) value.each do |field_name, v| self[field_name] = v end elsif value.is_a?(self.class) self.pointer.__copy_from__(value.pointer, self.size) end end