Skip to content

Commit cf78edd

Browse files
authored
Improve "offsetof" calculations (#90)
I need to get the offset of members inside sub structures. This patch adds sub-structure offset support for structs.
1 parent 8b9d699 commit cf78edd

File tree

2 files changed

+77
-16
lines changed

2 files changed

+77
-16
lines changed

lib/fiddle/struct.rb

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,53 @@ def CStruct.entity_class
1515

1616
def self.offsetof(name, members, types) # :nodoc:
1717
offset = 0
18-
index = 0
19-
member_index = members.index(name)
20-
21-
types.each { |type, count = 1|
22-
orig_offset = offset
23-
if type.respond_to?(:entity_class)
24-
align = type.alignment
25-
type_size = type.size
26-
else
27-
align = PackInfo::ALIGN_MAP[type]
28-
type_size = PackInfo::SIZE_MAP[type]
18+
worklist = name.split('.')
19+
this_type = self
20+
while search_name = worklist.shift
21+
index = 0
22+
member_index = members.index(search_name)
23+
24+
unless member_index
25+
# Possibly a sub-structure
26+
member_index = members.index { |member_name, _|
27+
member_name == search_name
28+
}
29+
return unless member_index
2930
end
30-
offset = PackInfo.align(orig_offset, align)
3131

32-
return offset if index == member_index
32+
types.each { |type, count = 1|
33+
orig_offset = offset
34+
if type.respond_to?(:entity_class)
35+
align = type.alignment
36+
type_size = type.size
37+
else
38+
align = PackInfo::ALIGN_MAP[type]
39+
type_size = PackInfo::SIZE_MAP[type]
40+
end
41+
42+
# Unions shouldn't advance the offset
43+
if this_type.entity_class == CUnionEntity
44+
type_size = 0
45+
end
3346

34-
offset += (type_size * count)
35-
index += 1
36-
}
47+
offset = PackInfo.align(orig_offset, align)
48+
49+
if worklist.empty?
50+
return offset if index == member_index
51+
else
52+
if index == member_index
53+
subtype = types[member_index]
54+
members = subtype.members
55+
types = subtype.types
56+
this_type = subtype
57+
break
58+
end
59+
end
60+
61+
offset += (type_size * count)
62+
index += 1
63+
}
64+
end
3765
nil
3866
end
3967

test/fiddle/test_c_struct_builder.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,45 @@
33
require_relative 'helper'
44
require 'fiddle/struct'
55
require 'fiddle/cparser'
6+
require 'fiddle/import'
67
rescue LoadError
78
end
89

910
module Fiddle
1011
class TestCStructBuilder < TestCase
1112
include Fiddle::CParser
13+
extend Fiddle::Importer
14+
15+
RBasic = struct ['void * flags',
16+
'void * klass' ]
17+
18+
19+
RObject = struct [
20+
{ 'basic' => RBasic },
21+
{ 'as' => union([
22+
{ 'heap'=> struct([ 'uint32_t numiv',
23+
'void * ivptr',
24+
'void * iv_index_tbl' ]) },
25+
'void *ary[3]' ])}
26+
]
27+
28+
29+
def test_basic_embedded_members
30+
assert_equal 0, RObject.offsetof("basic.flags")
31+
assert_equal Fiddle::SIZEOF_VOIDP, RObject.offsetof("basic.klass")
32+
end
33+
34+
def test_embedded_union_members
35+
assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as")
36+
assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap")
37+
assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap.numiv")
38+
assert_equal 3 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap.ivptr")
39+
assert_equal 4 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.heap.iv_index_tbl")
40+
end
41+
42+
def test_as_ary
43+
assert_equal 2 * Fiddle::SIZEOF_VOIDP, RObject.offsetof("as.ary")
44+
end
1245

1346
def test_offsetof
1447
types, members = parse_struct_signature(['int64_t i','char c'])

0 commit comments

Comments
 (0)