Class: FFI::StructEx

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

Defined Under Namespace

Classes: BitLayout

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ StructEx

Returns a new instance of StructEx.



93
94
95
96
97
98
99
100
# File 'lib/ffi/struct_ex/struct_ex.rb', line 93

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

Class Attribute Details

.bit_layoutsObject (readonly)

Returns the value of attribute bit_layouts.



51
52
53
# File 'lib/ffi/struct_ex/struct_ex.rb', line 51

def bit_layouts
  @bit_layouts
end

.bits_sizeObject (readonly)

Returns the value of attribute bits_size.



51
52
53
# File 'lib/ffi/struct_ex/struct_ex.rb', line 51

def bits_size
  @bits_size
end

Class Method Details

.alignmentObject



86
87
88
89
90
# File 'lib/ffi/struct_ex/struct_ex.rb', line 86

def alignment
  return super unless self.bit_layouts
  #FIXME consider 24 bits situation
  FFI.find_type("uint#{bytes_size * 8}".to_sym).alignment
end

.bit_fields(*descs) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ffi/struct_ex/struct_ex.rb', line 19

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

  bit_fields_class = Class.new(FFI::StructLayout::Field) do
    def initialize(name, offset, type)
      #TODO use a different native_type to avoid dummy field for struct
      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
    end

    self.struct_class = struct_class
  end
end

.bytes_sizeObject



82
83
84
# File 'lib/ffi/struct_ex/struct_ex.rb', line 82

def bytes_size
  (bits_size + 7) >> 3
end

.layout(*descs) ⇒ Object



53
54
55
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
# File 'lib/ffi/struct_ex/struct_ex.rb', line 53

def layout(*descs)
  if descs.size == 0 || !descs[1].is_a?(Integer)
    super(*descs)
    #@bits_size = self.size * 8
  else
    @bit_layouts = {}

    index = @bits_size = 0

    while index < descs.size
      bit_field_name, bits, texts = descs[index, 3]

      if texts.kind_of?(Hash)
        @bit_layouts[bit_field_name] = BitLayout.new(bit_field_name, bits, @bits_size, texts)
        index += 3
      else
        @bit_layouts[bit_field_name] = BitLayout.new(bit_field_name, bits, @bits_size)
        index += 2
      end

      @bits_size += bits
    end

    #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#{bytes_size * 8}".to_sym)
  end
end

Instance Method Details

#==(other) ⇒ Object



141
142
143
144
145
146
147
148
149
# File 'lib/ffi/struct_ex/struct_ex.rb', line 141

def ==(other)
  if other.is_a?(Integer)
    self.read == other
  elsif other.is_a?(Hash)
    other.all? {|k, v| self[k] == self.look_for_value(k, v)}
  else
    super
  end
end

#[](bit_field_name) ⇒ Object



102
103
104
105
106
107
108
109
# File 'lib/ffi/struct_ex/struct_ex.rb', line 102

def [](bit_field_name)
  return super unless self.class.bit_layouts && self.class.bit_layouts.keys.include?(bit_field_name)

  bit_layout = self.class.bit_layouts[bit_field_name]
  mask = ((1 << bit_layout.bits) - 1) << bit_layout.offset

  (self.read & mask) >> bit_layout.offset
end

#[]=(bit_field_name, value) ⇒ Object



111
112
113
114
115
116
117
118
119
120
# File 'lib/ffi/struct_ex/struct_ex.rb', line 111

def []=(bit_field_name, value)
  return super unless self.class.bit_layouts && self.class.bit_layouts.keys.include?(bit_field_name)

  value = look_for_value(bit_field_name, value)

  bit_layout = self.class.bit_layouts[bit_field_name]
  mask = ((1 << bit_layout.bits) - 1) << bit_layout.offset

  self.write((self.read & (-1 - mask)) | ((value << bit_layout.offset) & mask))
end

#look_for_value(bit_field_name, value) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/ffi/struct_ex/struct_ex.rb', line 151

def look_for_value(bit_field_name, value)
  #FIXME add error handling
  if value.kind_of?(Integer)
    value
  elsif value.kind_of?(String)
    #FIXME this requires texts hash to have downcase key
    value = value.downcase
    if self.class.bit_layouts[bit_field_name].texts && self.class.bit_layouts[bit_field_name].texts[value]
      self.class.bit_layouts[bit_field_name].texts[value]
    else
      case value
        when /^\d+$/
          value.to_i
        when /^0x[\da-fA-F]+$/
          value.to_i(16)
        when /^0b[01]+$/
          value.to_i(2)
      end
    end
  else
    value
  end
end

#readObject



132
133
134
135
# File 'lib/ffi/struct_ex/struct_ex.rb', line 132

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

#sizeObject



137
138
139
# File 'lib/ffi/struct_ex/struct_ex.rb', line 137

def size
  self.class.bytes_size
end

#write(value) ⇒ Object



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

def write(value)
  if value.is_a?(Integer)
    to_ptr.write_array_of_uint8(value.to_bytes(self.class.bytes_size))
  elsif value.is_a?(Hash)
    value.each do |bit_field_name, v|
      self[bit_field_name] = v if self.class.bit_layouts.keys.include? bit_field_name
    end
  end
end