Module: FFI::BitField::ClassMethods

Included in:
FFI::BitStruct, ManagedBitStruct
Defined in:
lib/ffi/bit_field/class_methods.rb

Overview

ClassMethods provides methods for defining bit field layouts. This module is extended by BitStruct and ManagedBitStruct classes.

Instance Method Summary collapse

Instance Method Details

#bit_field_layoutHash

Returns a hash of bit fields with detailed layout information.

Examples:

Get detailed bit field layout in a struct

class Flags < FFI::BitStruct
  layout \
    :value, :uint8

  bit_fields :value,
    :read,    1,
    :write,   1,
    :execute, 1,
    :unused,  5
end

Flags.bit_field_layout
# => {
#      :value => {
#        :read    => { :start => 0, :width => 1 },
#        :write   => { :start => 1, :width => 1 },
#        :execute => { :start => 2, :width => 1 },
#        :unused  => { :start => 3, :width => 5 }
#      }
#    }

Returns:

  • (Hash)

    A hash where keys are parent field names and values are hashes of bit field details



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/ffi/bit_field/class_methods.rb', line 60

def bit_field_layout
  return {} unless instance_variable_defined?(:@bit_field_hash_table)

  result = {}
  @bit_field_hash_table.each do |field_name, info|
    parent_name, start, width = info
    result[parent_name] ||= {}
    result[parent_name][field_name] = { start: start, width: width }
  end
  result
end

#bit_field_membersHash

Returns a hash of bit fields grouped by parent field.

Examples:

Get bit field members in a struct

class Flags < FFI::BitStruct
  layout \
    :value, :uint8

  bit_fields :value,
    :read,    1,
    :write,   1,
    :execute, 1,
    :unused,  5
end

Flags.bit_field_members  # => {:value => [:read, :write, :execute, :unused]}

Returns:

  • (Hash)

    A hash where keys are parent field names and values are arrays of bit field names



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/ffi/bit_field/class_methods.rb', line 23

def bit_field_members
  return {} unless instance_variable_defined?(:@bit_field_hash_table)

  result = {}
  @bit_field_hash_table.each do |field_name, info|
    parent_name = info[0]
    result[parent_name] ||= []
    result[parent_name] << field_name
  end
  result
end

#bit_field_offsetsHash

Returns a hash of bit fields with their bit offsets, grouped by parent field.

Examples:

Get bit field offsets in a struct

class Flags < FFI::BitStruct
  layout \
    :value, :uint8

  bit_fields :value,
    :read,    1,
    :write,   1,
    :execute, 1,
    :unused,  5
end

Flags.bit_field_offsets
# => {
#      :value => [[:read, 0], [:write, 1], [:execute, 2], [:unused, 3]]
#    }

Returns:

  • (Hash)

    A hash where keys are parent field names and values are arrays of [bit_field_name, bit_offset] pairs



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ffi/bit_field/class_methods.rb', line 92

def bit_field_offsets
  return {} unless instance_variable_defined?(:@bit_field_hash_table)

  result = {}

  # Get byte offsets of parent fields
  field_offsets = offsets.to_h

  # Process each bit field
  @bit_field_hash_table.each do |field_name, info|
    parent_name, start, _width = info

    # Get byte offset of parent field
    parent_offset = field_offsets[parent_name]
    next unless parent_offset

    # Convert byte offset to bit offset and add bit field's start position
    bit_offset = parent_offset * 8 + start

    # Add to result
    result[parent_name] ||= []
    result[parent_name] << [field_name, bit_offset]
  end

  # Return result
  result
end

#bit_fields(*layout_args) ⇒ Symbol Also known as: bit_field

Note:

The total bit width should not exceed the size of the parent field. For example, a :uint8 field can hold at most 8 bits.

Defines bit fields within a parent field.

Examples:

Define bit fields in an 8-bit integer

bit_fields :flags,
  :read,    1,  # 1 bit for read permission
  :write,   1,  # 1 bit for write permission
  :execute, 1,  # 1 bit for execute permission
  :unused,  5   # 5 unused bits

Parameters:

  • layout_args (Array)

    An array where the first element is the parent field name, followed by alternating field name and bit width pairs

Returns:

  • (Symbol)

    parent_name The name of the parent field



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ffi/bit_field/class_methods.rb', line 135

def bit_fields(*layout_args)
  # The reason for using class instance variable here instead of class variable
  # is not because class instance variables are clean,
  # but because sub-class of FFI::Struct cannot be inherited again.
  @bit_field_hash_table = {} unless instance_variable_defined?(:@bit_field_hash_table)

  parent_name = layout_args.shift.to_sym
  member_names = []
  widths = []
  layout_args.each_slice(2) do |name, width|
    member_names << name.to_sym
    widths << width.to_i
  end

  # Prevent redefining bit fields on the same parent field
  ensure_parent_not_defined(parent_name)

  # Validate total width against parent size
  validate_total_width(parent_name, widths)
  starts = bit_start_offsets(widths)
  member_names.zip(starts, widths).each do |name, start, width|
    @bit_field_hash_table[name] = [parent_name, start, width]
  end

  parent_name
end

#bit_fields_typed(parent_name, field_definitions) ⇒ Symbol

Note:

For fields with width 1 and type :bool, a “?” helper method is automatically created

Note:

The total bit width should not exceed the size of the parent field.

Defines typed bit fields within a parent field using hash syntax.

Examples:

Define typed bit fields with automatic boolean helpers

bit_fields_typed :flags,
  revoked: [1, :bool],      # Creates revoked? methods
  expired: [1, :bool],      # Creates expired? methods
  some_string: [4, :string] # Creates some_string method

Parameters:

  • parent_name (Symbol)

    The name of the parent field

  • field_definitions (Hash)

    A hash where keys are field names and values are arrays [width, type]

Returns:

  • (Symbol)

    parent_name The name of the parent field



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/ffi/bit_field/class_methods.rb', line 177

def bit_fields_typed(parent_name, field_definitions)
  @bit_field_hash_table = {} unless instance_variable_defined?(:@bit_field_hash_table)
  @bit_field_type_table = {} unless instance_variable_defined?(:@bit_field_type_table)

  parent_name = parent_name.to_sym
  member_names = []
  widths = []
  types = []

  field_definitions.each do |name, definition|
    width, type = definition
    member_names << name.to_sym
    widths << width.to_i
    types << type.to_sym
  end

  # Prevent redefining bit fields on the same parent field
  ensure_parent_not_defined(parent_name)

  # Validate total width against parent size
  validate_total_width(parent_name, widths)

  starts = bit_start_offsets(widths)

  member_names.zip(starts, widths, types).each do |name, start, width, type|
    @bit_field_hash_table[name] = [parent_name, start, width]
    @bit_field_type_table[name] = type

    # Generate "?" method for boolean fields with width 1
    next unless width == 1 && type == :bool

    define_method(:"#{name}?") do
      self[name] == 1
    end
  end

  parent_name
end