Class: FFI::StructEx

Inherits:
Struct
  • Object
show all
Defined in:
lib/ffi/struct_ex/struct_ex.rb

Constant Summary collapse

SIGNED_NUMBER_TYPES =
[FFI::Type::INT8, FFI::Type::INT16, FFI::Type::INT32, FFI::Type::INT64]
UNSIGNED_NUMBER_TYPES =
[FFI::Type::UINT8, FFI::Type::UINT16, FFI::Type::UINT32, FFI::Type::UINT64]
NUMBER_TYPES =
SIGNED_NUMBER_TYPES + UNSIGNED_NUMBER_TYPES

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ StructEx

Returns a new instance of StructEx.



163
164
165
166
167
168
169
170
# 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

Class Attribute Details

.field_specsObject (readonly)

Returns the value of attribute field_specs.



95
96
97
# File 'lib/ffi/struct_ex/struct_ex.rb', line 95

def field_specs
  @field_specs
end

Class Method Details

.bit_field(type, bits_size, bits_offset) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 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

.struct_ex(*field_specs) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 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

Instance Method Details

#==(other) ⇒ Object



187
188
189
190
191
192
193
# 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) ⇒ Object

Set field value



173
174
175
# 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) ⇒ Object

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

Parameters:

  • field_name (String, Symbol)

    name of the field

  • value (String, Integer, Object)

    value in descriptive form or native form

Returns:

  • (Object)

    value in native form



204
205
206
207
208
209
210
211
212
213
214
# 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) ⇒ Object



177
178
179
180
181
182
183
184
185
# 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