diff options
author | Aaron Patterson <tenderlove@ruby-lang.org> | 2021-08-19 16:43:56 -0700 |
---|---|---|
committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2021-08-24 16:18:22 +0900 |
commit | 0f1e8f38c9cd008eb24e6c957388a183eac910ca (patch) | |
tree | 094598db2b31941273035908096f84a147c2979c | |
parent | 8d2af51a783fa3490c158b110629110ab3257f56 (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.rb | 60 | ||||
-rw-r--r-- | test/fiddle/test_c_struct_builder.rb | 33 |
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']) |