Skip to content

Commit 4e3b60c

Browse files
tenderlovekou
andauthored
Add "offsetof" to Struct classes (#83)
* Add "offsetof" to Struct classes I need to get the offset of a member inside a struct without allocating the struct. This patch adds an "offsetof" class method to structs that are generated. The usage is like this: ```ruby MyStruct = struct [ "int64_t i", "char c", ] MyStruct.offsetof("i") # => 0 MyStruct.offsetof("c") # => 8 ``` * Update test/fiddle/test_c_struct_builder.rb Co-authored-by: Sutou Kouhei <kou@cozmixng.org> Co-authored-by: Sutou Kouhei <kou@cozmixng.org>
1 parent b150250 commit 4e3b60c

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

lib/fiddle/struct.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ def CStruct.entity_class
1313
CStructEntity
1414
end
1515

16+
def self.offsetof(name, members, types) # :nodoc:
17+
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]
29+
end
30+
offset = PackInfo.align(orig_offset, align)
31+
32+
return offset if index == member_index
33+
34+
offset += (type_size * count)
35+
index += 1
36+
}
37+
nil
38+
end
39+
1640
def each
1741
return enum_for(__function__) unless block_given?
1842

@@ -75,6 +99,10 @@ class CUnion
7599
def CUnion.entity_class
76100
CUnionEntity
77101
end
102+
103+
def self.offsetof(name, members, types) # :nodoc:
104+
0
105+
end
78106
end
79107

80108
# Wrapper for arrays within a struct
@@ -172,6 +200,21 @@ def create(klass, types, members)
172200
define_method(:to_i){ @entity.to_i }
173201
define_singleton_method(:types) { types }
174202
define_singleton_method(:members) { members }
203+
204+
# Return the offset of a struct member given its name.
205+
# For example:
206+
#
207+
# MyStruct = struct [
208+
# "int64_t i",
209+
# "char c",
210+
# ]
211+
#
212+
# MyStruct.offsetof("i") # => 0
213+
# MyStruct.offsetof("c") # => 8
214+
#
215+
define_singleton_method(:offsetof) { |name|
216+
klass.offsetof(name, members, types)
217+
}
175218
members.each{|name|
176219
name = name[0] if name.is_a?(Array) # name is a nested struct
177220
next if method_defined?(name)

test/fiddle/test_c_struct_builder.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# frozen_string_literal: true
2+
begin
3+
require_relative 'helper'
4+
require 'fiddle/struct'
5+
require 'fiddle/cparser'
6+
rescue LoadError
7+
end
8+
9+
module Fiddle
10+
class TestCStructBuilder < TestCase
11+
include Fiddle::CParser
12+
13+
def test_offsetof
14+
types, members = parse_struct_signature(['int64_t i','char c'])
15+
my_struct = Fiddle::CStructBuilder.create(Fiddle::CStruct, types, members)
16+
assert_equal 0, my_struct.offsetof("i")
17+
assert_equal Fiddle::SIZEOF_INT64_T, my_struct.offsetof("c")
18+
end
19+
20+
def test_offset_with_gap
21+
types, members = parse_struct_signature(['void *p', 'char c', 'long x'])
22+
my_struct = Fiddle::CStructBuilder.create(Fiddle::CStruct, types, members)
23+
24+
assert_equal PackInfo.align(0, ALIGN_VOIDP), my_struct.offsetof("p")
25+
assert_equal PackInfo.align(SIZEOF_VOIDP, ALIGN_CHAR), my_struct.offsetof("c")
26+
assert_equal SIZEOF_VOIDP + PackInfo.align(SIZEOF_CHAR, ALIGN_LONG), my_struct.offsetof("x")
27+
end
28+
29+
def test_union_offsetof
30+
types, members = parse_struct_signature(['int64_t i','char c'])
31+
my_struct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
32+
assert_equal 0, my_struct.offsetof("i")
33+
assert_equal 0, my_struct.offsetof("c")
34+
end
35+
end
36+
end if defined?(Fiddle)

0 commit comments

Comments
 (0)