ruby-changes:66773
From: Aaron <ko1@a...>
Date: Tue, 13 Jul 2021 19:38:27 +0900 (JST)
Subject: [ruby-changes:66773] 5c0d8c6369 (master): [ruby/fiddle] Add "offsetof" to Struct classes (https://github.com/ruby/fiddle/pull/83)
https://git.ruby-lang.org/ruby.git/commit/?id=5c0d8c6369 From 5c0d8c6369f92915bf99924f58f0763abe4f493e Mon Sep 17 00:00:00 2001 From: Aaron Patterson <tenderlove@r...> Date: Wed, 30 Jun 2021 17:35:04 -0700 Subject: [ruby/fiddle] Add "offsetof" to Struct classes (https://github.com/ruby/fiddle/pull/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@c...> https://github.com/ruby/fiddle/commit/4e3b60c5b6 Co-authored-by: Sutou Kouhei <kou@c...> --- ext/fiddle/lib/fiddle/struct.rb | 43 ++++++++++++++++++++++++++++++++++++ test/fiddle/test_c_struct_builder.rb | 36 ++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 test/fiddle/test_c_struct_builder.rb diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb index a766eba..2353edc 100644 --- a/ext/fiddle/lib/fiddle/struct.rb +++ b/ext/fiddle/lib/fiddle/struct.rb @@ -13,6 +13,30 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/struct.rb#L13 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 https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/struct.rb#L99 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 https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/struct.rb#L200 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 0000000..187424c --- /dev/null +++ b/test/fiddle/test_c_struct_builder.rb @@ -0,0 +1,36 @@ https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_c_struct_builder.rb#L1 +# 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) -- cgit v1.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/