Class: FFI::StructEx

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

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ StructEx

Returns a new instance of StructEx.



115
116
117
118
119
120
121
122
# File 'lib/ffi/struct_ex/struct_ex.rb', line 115

def initialize(options = {})
  if options.is_a?(FFI::Pointer)
    super(options)
  else
    super()
    write(options)
  end
end

Class Attribute Details

.bits_sizeObject (readonly)

Returns the value of attribute bits_size.



65
66
67
# File 'lib/ffi/struct_ex/struct_ex.rb', line 65

def bits_size
  @bits_size
end

.field_specsObject (readonly)

Returns the value of attribute field_specs.



65
66
67
# File 'lib/ffi/struct_ex/struct_ex.rb', line 65

def field_specs
  @field_specs
end

.has_bit_fieldObject (readonly)

Returns the value of attribute has_bit_field.



65
66
67
# File 'lib/ffi/struct_ex/struct_ex.rb', line 65

def has_bit_field
  @has_bit_field
end

Class Method Details

.bit_fields(*field_specs) ⇒ Object



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
55
56
57
58
59
60
61
62
63
# File 'lib/ffi/struct_ex/struct_ex.rb', line 30

def bit_fields(*field_specs)
  struct_class = Class.new(StructEx) do
    layout(*field_specs)
  end

  bit_fields_class = Class.new(FFI::StructLayout::Field) do
    def initialize(name, offset, type)
      super(name, offset, FFI::Type::Struct.new(self.class.struct_class))
    end

    def get(ptr)
      #self.class.struct_class == type.struct_class
      self.class.struct_class.new(ptr.slice(self.offset, self.size))
    end

    def put(ptr, value)
      self.class.struct_class.new(ptr.slice(self.offset, self.size)).write(value)
    end

    class << self
      attr_accessor :struct_class

      def alignment
        struct_class.alignment
      end

      def size
        struct_class.size
      end
    end

    self.struct_class = struct_class
  end
end

.layout(*field_specs) ⇒ Object



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
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ffi/struct_ex/struct_ex.rb', line 67

def layout(*field_specs)
  return super if field_specs.size == 0

  field_spec_class = ::Struct.new(:name, :type, :bits_offset, :descriptors)

  @field_specs = {}

  i = bits_offset = 0

  while i < field_specs.size
    field_name, type = field_specs[i, 2]
    i += 2

    unless type.is_a?(Integer)
      type = find_field_type(type)
      bits_size = type.size * 8

      if field_specs[i].is_a?(Integer)
        bits_offset = field_specs[i] * 8
        i += 1
      end
    else
      bits_size = type
    end

    if field_specs[i].is_a?(Hash)
      descriptors = field_specs[i]
      i += 1
    else
      descriptors = {}
    end

    @field_specs[field_name] = field_spec_class.new(field_name, type, bits_offset, descriptors)
    bits_offset += bits_size
  end

  @has_bit_field = @field_specs.any? {|field_name, field_spec| field_spec.type.is_a?(Integer)}

  if @has_bit_field
    #FIXME consider 24 bits situation or larger than 32 bits
    #FIXME remove dummy field or have a better name for this field
    super(:dummy, "uint#{(bits_offset + 7) & (-1 << 3)}".to_sym)
  else
    super(*field_specs.reject {|field_spec| field_spec.is_a?(Hash)})
  end
end

Instance Method Details

#==(other) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
# File 'lib/ffi/struct_ex/struct_ex.rb', line 160

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) ⇒ Object



124
125
126
127
128
129
130
131
# File 'lib/ffi/struct_ex/struct_ex.rb', line 124

def [](field_name)
  return super unless self.class.has_bit_field

  field_spec = self.class.field_specs[field_name]
  mask = ((1 << field_spec.type) - 1) << field_spec.bits_offset

  (self.read & mask) >> field_spec.bits_offset
end

#[]=(field_name, value) ⇒ Object

Set field value



134
135
136
137
138
139
140
141
142
143
# File 'lib/ffi/struct_ex/struct_ex.rb', line 134

def []=(field_name, value)
  value = map_field_value(field_name, value)

  return super(field_name, value) unless self.class.has_bit_field

  field_spec = self.class.field_specs[field_name]
  mask = ((1 << field_spec.type) - 1) << field_spec.bits_offset

  self.write((self.read & (-1 - mask)) | ((value << field_spec.bits_offset) & mask))
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



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

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) || FFI::StructLayoutBuilder::NUMBER_TYPES.include?(type)) && value.is_a?(String)

  value
end

#readObject



155
156
157
158
# File 'lib/ffi/struct_ex/struct_ex.rb', line 155

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



145
146
147
148
149
150
151
152
153
# File 'lib/ffi/struct_ex/struct_ex.rb', line 145

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