summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2021-08-19 16:43:56 -0700
committerNobuyoshi Nakada <nobu@ruby-lang.org>2021-08-24 16:18:22 +0900
commit0f1e8f38c9cd008eb24e6c957388a183eac910ca (patch)
tree094598db2b31941273035908096f84a147c2979c
parent8d2af51a783fa3490c158b110629110ab3257f56 (diff)
[ruby/fiddle] Improve "offsetof" calculations (https://github.com/ruby/fiddle/pull/90)
I need to get the offset of members inside sub structures. This patch adds sub-structure offset support for structs. https://github.com/ruby/fiddle/commit/cf78eddbb6
-rw-r--r--ext/fiddle/lib/fiddle/struct.rb60
-rw-r--r--test/fiddle/test_c_struct_builder.rb33
2 files changed, 77 insertions, 16 deletions
diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb
index 2353edcc94..6d05bbd742 100644
--- a/ext/fiddle/lib/fiddle/struct.rb
+++ b/ext/fiddle/lib/fiddle/struct.rb
@@ -15,25 +15,53 @@ module Fiddle
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]
+ worklist = name.split('.')
+ this_type = self
+ while search_name = worklist.shift
+ index = 0
+ member_index = members.index(search_name)
+
+ unless member_index
+ # Possibly a sub-structure
+ member_index = members.index { |member_name, _|
+ member_name == search_name
+ }
+ return unless member_index
end
- offset = PackInfo.align(orig_offset, align)
- return offset if index == member_index
+ 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
+
+ # Unions shouldn't advance the offset
+ if this_type.entity_class == CUnionEntity
+ type_size = 0
+ end
- offset += (type_size * count)
- index += 1
- }
+ offset = PackInfo.align(orig_offset, align)
+
+ if worklist.empty?
+ return offset if index == member_index
+ else
+ if index == member_index
+ subtype = types[member_index]
+ members = subtype.members
+ types = subtype.types
+ this_type = subtype
+ break
+ end
+ end
+
+ offset += (type_size * count)
+ index += 1
+ }
+ end
nil
end
diff --git a/test/fiddle/test_c_struct_builder.rb b/test/fiddle/test_c_struct_builder.rb
index 187424c92d..ca44c6cf7a 100644
--- a/test/fiddle/test_c_struct_builder.rb
+++ b/test/fiddle/test_c_struct_builder.rb
@@ -3,12 +3,45 @@ begin
require_relative 'helper'
require 'fiddle/struct'
require 'fiddle/cparser'
+ require 'fiddle/import'
rescue LoadError
end
module Fiddle
class TestCStructBuilder < TestCase
include Fiddle::CParser
+ extend Fiddle::Importer
+
+ RBasic = struct ['void * flags',
+ 'void * klass' ]
+
+
+ RObject = struct [
+ { 'basic' => RBasic },
+ { 'as' => union([
+ { 'heap'=> struct([ 'uint32_t numiv',
+ 'void * ivptr',
+ 'void * iv_index_tbl' ]) },
+ 'void *ary[3]' ])}
+ ]
+
+
+ def test_basic_embedded_members
+ assert_equal 0, RObject.offsetof("basic.flags")
+ assert_equal Fiddle::SIZEOF_VOIDP, RObject.offsetof("basic.klass")
+ end
+
+ def test_embedded_union_members
+ assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as")
+ assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap")
+ assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap.numiv")
+ assert_equal 3 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap.ivptr")
+ assert_equal 4 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap.iv_index_tbl")
+ end
+
+ def test_as_ary
+ assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.ary")
+ end
def test_offsetof
types, members = parse_struct_signature(['int64_t i','char c'])