summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/fiddle/lib/fiddle/struct.rb43
-rw-r--r--test/fiddle/test_c_struct_builder.rb36
2 files changed, 79 insertions, 0 deletions
diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb
index a766eba83b..2353edcc94 100644
--- a/ext/fiddle/lib/fiddle/struct.rb
+++ b/ext/fiddle/lib/fiddle/struct.rb
@@ -13,6 +13,30 @@ module Fiddle
CStructEntity
end
+ def self.offsetof(name, members, types) # :nodoc:
+ offset = 0
+ index = 0
+ member_index = members.index(name)
+
+ types.each { |type, count = 1|
+ orig_offset = offset
+ if type.respond_to?(:entity_class)
+ align = type.alignment
+ type_size = type.size
+ else
+ align = PackInfo::ALIGN_MAP[type]
+ type_size = PackInfo::SIZE_MAP[type]
+ end
+ offset = PackInfo.align(orig_offset, align)
+
+ return offset if index == member_index
+
+ offset += (type_size * count)
+ index += 1
+ }
+ nil
+ end
+
def each
return enum_for(__function__) unless block_given?
@@ -75,6 +99,10 @@ module Fiddle
def CUnion.entity_class
CUnionEntity
end
+
+ def self.offsetof(name, members, types) # :nodoc:
+ 0
+ end
end
# Wrapper for arrays within a struct
@@ -172,6 +200,21 @@ module Fiddle
define_method(:to_i){ @entity.to_i }
define_singleton_method(:types) { types }
define_singleton_method(:members) { members }
+
+ # Return the offset of a struct member given its name.
+ # For example:
+ #
+ # MyStruct = struct [
+ # "int64_t i",
+ # "char c",
+ # ]
+ #
+ # MyStruct.offsetof("i") # => 0
+ # MyStruct.offsetof("c") # => 8
+ #
+ define_singleton_method(:offsetof) { |name|
+ klass.offsetof(name, members, types)
+ }
members.each{|name|
name = name[0] if name.is_a?(Array) # name is a nested struct
next if method_defined?(name)
diff --git a/test/fiddle/test_c_struct_builder.rb b/test/fiddle/test_c_struct_builder.rb
new file mode 100644
index 0000000000..187424c92d
--- /dev/null
+++ b/test/fiddle/test_c_struct_builder.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+ require 'fiddle/struct'
+ require 'fiddle/cparser'
+rescue LoadError
+end
+
+module Fiddle
+ class TestCStructBuilder < TestCase
+ include Fiddle::CParser
+
+ def test_offsetof
+ types, members = parse_struct_signature(['int64_t i','char c'])
+ my_struct = Fiddle::CStructBuilder.create(Fiddle::CStruct, types, members)
+ assert_equal 0, my_struct.offsetof("i")
+ assert_equal Fiddle::SIZEOF_INT64_T, my_struct.offsetof("c")
+ end
+
+ def test_offset_with_gap
+ types, members = parse_struct_signature(['void *p', 'char c', 'long x'])
+ my_struct = Fiddle::CStructBuilder.create(Fiddle::CStruct, types, members)
+
+ assert_equal PackInfo.align(0, ALIGN_VOIDP), my_struct.offsetof("p")
+ assert_equal PackInfo.align(SIZEOF_VOIDP, ALIGN_CHAR), my_struct.offsetof("c")
+ assert_equal SIZEOF_VOIDP + PackInfo.align(SIZEOF_CHAR, ALIGN_LONG), my_struct.offsetof("x")
+ end
+
+ def test_union_offsetof
+ types, members = parse_struct_signature(['int64_t i','char c'])
+ my_struct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
+ assert_equal 0, my_struct.offsetof("i")
+ assert_equal 0, my_struct.offsetof("c")
+ end
+ end
+end if defined?(Fiddle)