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]

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ StructEx

Returns a new instance of StructEx.



181
182
183
184
185
186
187
188
# File 'lib/ffi/struct_ex/struct_ex.rb', line 181

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.



103
104
105
# File 'lib/ffi/struct_ex/struct_ex.rb', line 103

def field_specs
  @field_specs
end

Class Method Details

.bit_field(type, bits_size, bits_offset) ⇒ Object



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
94
95
96
97
98
99
100
101
# File 'lib/ffi/struct_ex/struct_ex.rb', line 64

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

.bit_fields(*field_specs) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/ffi/struct_ex/struct_ex.rb', line 37

def bit_fields(*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



210
211
212
213
214
215
216
217
218
219
220
# File 'lib/ffi/struct_ex/struct_ex.rb', line 210

def ==(other)
  if other.is_a?(Integer)
    self.read == other
  elsif other.is_a?(String)
    self.==(other.to_dec)
  elsif 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



191
192
193
# File 'lib/ffi/struct_ex/struct_ex.rb', line 191

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



231
232
233
234
235
236
237
238
239
240
241
# File 'lib/ffi/struct_ex/struct_ex.rb', line 231

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

#readObject



205
206
207
208
# File 'lib/ffi/struct_ex/struct_ex.rb', line 205

def read
  bytes = to_ptr.read_array_of_uint8(self.class.size)
  bytes.reverse.inject(0) {|value, n| (value << 8) | n}
end

#write(value) ⇒ Object



195
196
197
198
199
200
201
202
203
# File 'lib/ffi/struct_ex/struct_ex.rb', line 195

def write(value)
  if value.is_a?(Integer)
    to_ptr.write_array_of_uint8(value.to_bytes(self.class.size))
  elsif value.is_a?(Hash)
    value.each do |field_name, v|
      self[field_name] = v
    end
  end
end