[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]