ffi-bitfield 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e82e912397a405df792824503984be18325c6c22b76307a0fc9d7fe65c5a95f
4
- data.tar.gz: a3bbf7a5ecdcba6e4627c80e9a108788e281fc18f9d4abededf5d47833b0783d
3
+ metadata.gz: da1a5728a9f03382d8de48da89ad38e95ddec9e493288b5f5954fc0eeed34f24
4
+ data.tar.gz: f14ac752955f7a13a0753099cb85b46ff6e7d8bb20f559baea397ad6fa10375d
5
5
  SHA512:
6
- metadata.gz: 76c80c9d0444042ad7ca1b8c28b084562c84b9ac843f0f23a3253444fefee3f4a959da752395d4fa43fc663aab4b1854bb5bd3e21b2550fca2b5c4cc8560ad4a
7
- data.tar.gz: 33b72012820da5d15fcf1f753cd2a1203c3bd467b0d4596b429e7c46ba485bee91d45424142c4411996545054eb8f3bd368d53fa477cf7bde330c680689a9276
6
+ metadata.gz: 94d9b4e73951519a5f3af827ee4d3131891cf0586f7c01f1a7e83057fcbd27580e3edf61cc5176190ed9fc86feb73e392b122c5c7ea75ba93333609d9bc5bfe1
7
+ data.tar.gz: 1c652488233f533fb78e2724cb9070e81dd5cf8915e241676f7c60b8e8ecfb4de1c4fb651daf0e2f993d340fbc3a343c9ad4acd9dbebaa08308981f7af9e77c9
data/README.md CHANGED
@@ -70,6 +70,42 @@ s[:y] = 1
70
70
  p s[:a] # 64
71
71
  ```
72
72
 
73
+ ### Inspecting Bit Fields
74
+
75
+ You can use the `bit_field_members` method to get a hash of bit fields grouped by parent field:
76
+
77
+ ```ruby
78
+ class Flags < FFI::BitStruct
79
+ layout \
80
+ :value, :uint8
81
+
82
+ bit_fields :value,
83
+ :read, 1,
84
+ :write, 1,
85
+ :execute, 1,
86
+ :unused, 5
87
+ end
88
+
89
+ p Flags.bit_field_members
90
+ # => {:value=>[:read, :write, :execute, :unused]}
91
+ ```
92
+
93
+ For more detailed information, you can use the `bit_field_layout` method:
94
+
95
+ ```ruby
96
+ p Flags.bit_field_layout
97
+ # => {
98
+ # :value => {
99
+ # :read => { :start => 0, :width => 1 },
100
+ # :write => { :start => 1, :width => 1 },
101
+ # :execute => { :start => 2, :width => 1 },
102
+ # :unused => { :start => 3, :width => 5 }
103
+ # }
104
+ # }
105
+ ```
106
+
107
+ These methods are useful for custom pretty printing or introspection of your bit struct classes.
108
+
73
109
  ### Loading
74
110
 
75
111
  ```ruby
@@ -99,7 +135,7 @@ bundle exec rake test
99
135
  Your feedback is important.
100
136
 
101
137
  ffi-bitfield is a library under development, so even small improvements like typofix are welcome! Please feel free to send us your pull requests.
102
- Bug reports and pull requests are welcome on GitHub at https://github.com/kojix2/bitstruct.
138
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kojix2/ffi-bitfield.
103
139
 
104
140
  Do you need commit rights to my repository?
105
141
  Do you want to get admin rights and take over the project?
@@ -2,10 +2,90 @@
2
2
 
3
3
  module FFI
4
4
  module BitField
5
- # Layout provides the `bit_fields` method for registering field members.
5
+ # Layout provides methods for defining bit field layouts.
6
+ # This module is extended by BitStruct and ManagedBitStruct classes.
6
7
  module Layout
7
- # @param [Array] layout_args
8
- # @return [Symbol] parent_name
8
+ # Returns a hash of bit fields grouped by parent field.
9
+ #
10
+ # @return [Hash] A hash where keys are parent field names and values are arrays of bit field names
11
+ #
12
+ # @example Get bit field members in a struct
13
+ # class Flags < FFI::BitStruct
14
+ # layout \
15
+ # :value, :uint8
16
+ #
17
+ # bit_fields :value,
18
+ # :read, 1,
19
+ # :write, 1,
20
+ # :execute, 1,
21
+ # :unused, 5
22
+ # end
23
+ #
24
+ # Flags.bit_field_members # => {:value => [:read, :write, :execute, :unused]}
25
+ def bit_field_members
26
+ return {} unless instance_variable_defined?(:@bit_field_hash_table)
27
+
28
+ result = {}
29
+ @bit_field_hash_table.each do |field_name, info|
30
+ parent_name = info[0]
31
+ result[parent_name] ||= []
32
+ result[parent_name] << field_name
33
+ end
34
+ result
35
+ end
36
+
37
+ # Returns a hash of bit fields with detailed layout information.
38
+ #
39
+ # @return [Hash] A hash where keys are parent field names and values are hashes of bit field details
40
+ #
41
+ # @example Get detailed bit field layout in a struct
42
+ # class Flags < FFI::BitStruct
43
+ # layout \
44
+ # :value, :uint8
45
+ #
46
+ # bit_fields :value,
47
+ # :read, 1,
48
+ # :write, 1,
49
+ # :execute, 1,
50
+ # :unused, 5
51
+ # end
52
+ #
53
+ # Flags.bit_field_layout
54
+ # # => {
55
+ # # :value => {
56
+ # # :read => { :start => 0, :width => 1 },
57
+ # # :write => { :start => 1, :width => 1 },
58
+ # # :execute => { :start => 2, :width => 1 },
59
+ # # :unused => { :start => 3, :width => 5 }
60
+ # # }
61
+ # # }
62
+ def bit_field_layout
63
+ return {} unless instance_variable_defined?(:@bit_field_hash_table)
64
+
65
+ result = {}
66
+ @bit_field_hash_table.each do |field_name, info|
67
+ parent_name, start, width = info
68
+ result[parent_name] ||= {}
69
+ result[parent_name][field_name] = { start: start, width: width }
70
+ end
71
+ result
72
+ end
73
+
74
+ # Defines bit fields within a parent field.
75
+ #
76
+ # @param [Array] layout_args An array where the first element is the parent field name,
77
+ # followed by alternating field name and bit width pairs
78
+ # @return [Symbol] parent_name The name of the parent field
79
+ #
80
+ # @example Define bit fields in an 8-bit integer
81
+ # bit_fields :flags,
82
+ # :read, 1, # 1 bit for read permission
83
+ # :write, 1, # 1 bit for write permission
84
+ # :execute, 1, # 1 bit for execute permission
85
+ # :unused, 5 # 5 unused bits
86
+ #
87
+ # @note The total bit width should not exceed the size of the parent field.
88
+ # For example, a :uint8 field can hold at most 8 bits.
9
89
  def bit_fields(*layout_args)
10
90
  # The reason for using class instance variable here instead of class variable
11
91
  # is not because class instance variables are clean,
@@ -2,36 +2,95 @@
2
2
 
3
3
  module FFI
4
4
  module BitField
5
- # Properties provides methods to read and write bit fields.
5
+ # Property provides methods for reading and writing bit field values.
6
+ # This module is included in BitStruct and ManagedBitStruct classes.
6
7
  module Property
7
- # @param [Symbol] member_name
8
- # @return [Integer] value
8
+ # Reads a value from a bit field or regular field.
9
+ #
10
+ # @param [Symbol] member_name The name of the field to read
11
+ # @return [Integer] The value of the field
12
+ #
13
+ # @example Reading a bit field
14
+ # struct[:flag1] # => 1
9
15
  def [](member_name)
10
16
  parent_name, start, width = member_value_info(member_name)
11
17
  if parent_name
12
18
  value = get_member_value(parent_name)
13
- value[start, width]
19
+ (value >> start) & ((1 << width) - 1)
14
20
  else
15
21
  get_member_value(member_name)
16
22
  end
17
23
  end
18
24
 
25
+ # Writes a value to a bit field or regular field.
26
+ #
27
+ # @param [Symbol] member_name The name of the field to write
28
+ # @param [Integer] value The value to write
29
+ # @return [Integer] The written value
30
+ # @raise [ArgumentError] If the value is too large for the bit field
31
+ # @raise [ArgumentError] If the value is too small (negative) for the bit field
32
+ # @raise [ArgumentError] If the member name is not a valid bit field
33
+ # @raise [TypeError] If the value is not an Integer
34
+ #
35
+ # @example Writing to a bit field
36
+ # struct[:flag1] = 1
37
+ # @example Writing a negative value (bit-flipped)
38
+ # struct[:field] = -1 # Sets all bits to 1
19
39
  def []=(member_name, value)
20
- parent_name, start, width = member_value_info(member_name)
21
- if parent_name
22
- raise ArgumentError, "Value #{value} is larger than bit_length: #{width}" if value.bit_length > width
40
+ # Ensure value is an Integer
41
+ raise TypeError, "Value must be an Integer, got #{value.class}" unless value.is_a?(Integer)
23
42
 
24
- parent_value = get_member_value(parent_name)
25
- new_value = (((1 << width) - 1 << start) & parent_value ^ parent_value) |
26
- (value << start)
27
- set_member_value(parent_name, new_value)
28
- else
29
- set_member_value(member_name, value)
43
+ # Get bit field information
44
+ field_info = member_value_info(member_name)
45
+
46
+ # If not a bit field, delegate to regular field setter
47
+ return set_member_value(member_name, value) unless field_info
48
+
49
+ # Extract bit field information
50
+ parent_name, start, width = field_info
51
+
52
+ # Calculate max value for this bit width
53
+ max_value = (1 << width) - 1
54
+
55
+ # Handle negative values by bit-flipping
56
+ if value.negative?
57
+ # For negative values, we interpret them as bit-flipped positive values
58
+ # For example, with 4 bits, -1 becomes 1111 (15), -2 becomes 1110 (14), etc.
59
+
60
+ # Check if the negative value is within range
61
+ # For bit-flipping, valid range is -(2^n) to -1
62
+ min_value = -(1 << width)
63
+ if value < min_value
64
+ raise ArgumentError, "Value #{value} is too small for bit_length: #{width}, minimum is #{min_value}"
65
+ end
66
+
67
+ # Convert negative value to bit-flipped positive value
68
+ # -1 -> 15, -2 -> 14, etc.
69
+ value = max_value + value + 1
70
+
71
+ # Sanity check after conversion
72
+ if value.negative? || value > max_value
73
+ raise ArgumentError, "Internal error: converted value #{value} is out of range for bit_length: #{width}"
74
+ end
75
+ elsif value > max_value
76
+ # For positive values, check if they fit in the bit width
77
+ raise ArgumentError, "Value #{value} is too large for bit_length: #{width}, maximum is #{max_value}"
30
78
  end
79
+
80
+ # Update the parent field with the new bit field value
81
+ parent_value = get_member_value(parent_name)
82
+ mask = ((1 << width) - 1) << start
83
+ new_value = (parent_value & ~mask) | ((value & ((1 << width) - 1)) << start)
84
+
85
+ set_member_value(parent_name, new_value)
31
86
  end
32
87
 
33
88
  private
34
89
 
90
+ # Gets information about a bit field member.
91
+ #
92
+ # @param [Symbol] member_name The name of the bit field
93
+ # @return [Array, nil] An array containing [parent_name, start_bit, width] or nil if not a bit field
35
94
  def member_value_info(member_name)
36
95
  self.class.instance_variable_get(:@bit_field_hash_table)[member_name]
37
96
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module FFI
4
4
  module BitField
5
- VERSION = '0.0.7'
5
+ # Current version of the ffi-bitfield gem.
6
+ VERSION = '0.0.9'
6
7
  end
7
8
  end
data/lib/ffi/bit_field.rb CHANGED
@@ -4,8 +4,26 @@ require_relative 'bit_field/version'
4
4
  require_relative 'bit_struct'
5
5
  require_relative 'managed_bit_struct'
6
6
 
7
+ # Foreign Function Interface module for Ruby.
8
+ # This is the main namespace for the Ruby-FFI library.
7
9
  module FFI
8
- # This module is just a namespace.
10
+ # BitField provides bit field functionality for Ruby-FFI.
11
+ # It allows defining, reading, and writing bit fields within FFI structs.
12
+ #
13
+ # @example Basic usage
14
+ # class MyStruct < FFI::BitStruct
15
+ # layout \
16
+ # :flags, :uint8
17
+ #
18
+ # bit_fields :flags,
19
+ # :flag1, 1,
20
+ # :flag2, 1,
21
+ # :value, 6
22
+ # end
23
+ #
24
+ # struct = MyStruct.new
25
+ # struct[:flag1] = 1
26
+ # struct[:value] = 42
9
27
  module BitField
10
28
  end
11
29
  end
@@ -6,7 +6,25 @@ require_relative 'bit_field/layout'
6
6
  require_relative 'bit_field/property'
7
7
 
8
8
  module FFI
9
- # Subclass of FFI::Struct that support bit fields.
9
+ # Subclass of FFI::Struct that supports bit fields.
10
+ # Allows defining and accessing individual bits within integer fields.
11
+ #
12
+ # @example Define a struct with bit fields
13
+ # class Flags < FFI::BitStruct
14
+ # layout \
15
+ # :value, :uint8
16
+ #
17
+ # bit_fields :value,
18
+ # :read, 1, # 1 bit for read permission
19
+ # :write, 1, # 1 bit for write permission
20
+ # :execute, 1, # 1 bit for execute permission
21
+ # :unused, 5 # 5 unused bits
22
+ # end
23
+ #
24
+ # flags = Flags.new
25
+ # flags[:read] = 1
26
+ # flags[:write] = 1
27
+ # puts flags[:value] # => 3
10
28
  class BitStruct < Struct
11
29
  # [] is defined in FFI::Struct
12
30
  alias get_member_value []
@@ -6,7 +6,28 @@ require_relative 'bit_field/layout'
6
6
  require_relative 'bit_field/property'
7
7
 
8
8
  module FFI
9
- # Subclass of FFI::ManagedStruct that support bit fields.
9
+ # Subclass of FFI::ManagedStruct that supports bit fields.
10
+ # Combines memory management with bit field functionality.
11
+ #
12
+ # Use this class when you need automatic memory management for your structs
13
+ # with bit fields. You must implement the self.release method to handle
14
+ # memory cleanup.
15
+ #
16
+ # @example Define a managed struct with bit fields
17
+ # class ManagedFlags < FFI::ManagedBitStruct
18
+ # layout \
19
+ # :value, :uint8
20
+ #
21
+ # bit_fields :value,
22
+ # :read, 1,
23
+ # :write, 1,
24
+ # :execute, 1,
25
+ # :unused, 5
26
+ #
27
+ # def self.release(ptr)
28
+ # # Custom memory cleanup code
29
+ # end
30
+ # end
10
31
  class ManagedBitStruct < ManagedStruct
11
32
  # [] is defined in FFI::Struct
12
33
  alias get_member_value []
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi-bitfield
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - kojix2
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-16 00:00:00.000000000 Z
11
+ date: 2025-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -43,7 +43,7 @@ homepage: https://github.com/kojix2/ffi-bitfield
43
43
  licenses:
44
44
  - MIT
45
45
  metadata: {}
46
- post_install_message:
46
+ post_install_message:
47
47
  rdoc_options: []
48
48
  require_paths:
49
49
  - lib
@@ -58,8 +58,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
58
  - !ruby/object:Gem::Version
59
59
  version: '0'
60
60
  requirements: []
61
- rubygems_version: 3.3.7
62
- signing_key:
61
+ rubygems_version: 3.5.22
62
+ signing_key:
63
63
  specification_version: 4
64
64
  summary: bit fields for Ruby-FFI
65
65
  test_files: []