From d22cb12ff5a7f26ba9675445f4ba5add43f3cebd Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Wed, 17 Jun 2020 00:15:08 +0100 Subject: [PATCH 01/15] Clarify what CStructEntity and CUnionEntity are - they're pointers --- lib/fiddle/struct.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 259903d2..4f37fc55 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -110,7 +110,7 @@ def new_class.malloc() module_function :create end - # A C struct wrapper + # A pointer to a C structure class CStructEntity < Fiddle::Pointer include PackInfo include ValueUtil @@ -266,7 +266,7 @@ def to_s() # :nodoc: end end - # A C union wrapper + # A pointer to a C union class CUnionEntity < CStructEntity include PackInfo From a265c08207a6ceaefca584503456872fa507fcb5 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Wed, 17 Jun 2020 00:15:19 +0100 Subject: [PATCH 02/15] Fix struct/union typos --- test/fiddle/test_c_struct_entry.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb index 9ce75cf0..1490fd0b 100644 --- a/test/fiddle/test_c_struct_entry.rb +++ b/test/fiddle/test_c_struct_entry.rb @@ -43,15 +43,15 @@ def test_class_size_with_count end def test_set_ctypes - union = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE - union.assign_names %w[int long] + struct = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE + struct.assign_names %w[int long] # this test is roundabout because the stored ctypes are not accessible - union['long'] = 1 - union['int'] = 2 + struct['long'] = 1 + struct['int'] = 2 - assert_equal 1, union['long'] - assert_equal 2, union['int'] + assert_equal 1, struct['long'] + assert_equal 2, struct['int'] end def test_aref_pointer_array From 181a82386fde3d8478c3bfca13a1df399977a291 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Wed, 17 Jun 2020 00:56:11 +0100 Subject: [PATCH 03/15] Add new pointer memory management interfaces to structs and unions --- lib/fiddle/struct.rb | 55 ++++++++++-- test/fiddle/test_c_struct_entry.rb | 136 ++++++++++++++++++++++++----- test/fiddle/test_c_union_entity.rb | 15 ++-- test/fiddle/test_import.rb | 45 ++-------- 4 files changed, 178 insertions(+), 73 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 4f37fc55..56d78e4e 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -62,7 +62,7 @@ module CStructBuilder # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an # easy-to-use manner. # - # Example: + # Examples: # # require 'fiddle/struct' # require 'fiddle/cparser' @@ -73,6 +73,17 @@ module CStructBuilder # # MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members) # + # MyStruct.malloc(Fiddle::RUBY_FREE) do |obj| + # ... + # end + # + # obj = MyStruct.malloc(Fiddle::RUBY_FREE) + # begin + # ... + # ensure + # obj.call_free + # end + # # obj = MyStruct.malloc # begin # ... @@ -82,8 +93,8 @@ module CStructBuilder # def create(klass, types, members) new_class = Class.new(klass){ - define_method(:initialize){|addr| - @entity = klass.entity_class.new(addr, types) + define_method(:initialize){|addr, func = nil| + @entity = klass.entity_class.new(addr, types, func) @entity.assign_names(members) } define_method(:[]) { |*args| @entity.send(:[], *args) } @@ -100,9 +111,19 @@ def create(klass, types, members) def new_class.size() #{size} end - def new_class.malloc() + def new_class.malloc(func = nil) addr = Fiddle.malloc(#{size}) - new(addr) + struct = new(addr, func) + if block_given? + raise ArgumentError, 'a free function must be supplied to malloc when it is called with a block' unless func + begin + yield struct + ensure + struct.to_ptr.call_free + end + else + struct + end end EOS return new_class @@ -119,8 +140,18 @@ class CStructEntity < Fiddle::Pointer # # See Fiddle::Pointer.malloc for memory management issues. def CStructEntity.malloc(types, func = nil) - addr = Fiddle.malloc(CStructEntity.size(types)) - CStructEntity.new(addr, types, func) + addr = Fiddle.malloc(self.size(types)) + struct = new(addr, types, func) + if block_given? + raise ArgumentError, 'a free function must be supplied to Fiddle::CStructEntity.malloc when it is called with a block' unless func + begin + yield struct + ensure + struct.call_free + end + else + struct + end end # Returns the offset for the packed sizes for the given +types+. @@ -152,6 +183,9 @@ def CStructEntity.size(types) # # See also Fiddle::Pointer.new def initialize(addr, types, func = nil) + if func && addr.is_a?(Pointer) && addr.free + raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?' + end set_ctypes(types) super(addr, @size, func) end @@ -261,6 +295,10 @@ def []=(*args) end end + def size=(new_size) # :nodoc: + raise ArgumentError, 'cannot change the size of a struct' + end + def to_s() # :nodoc: super(@size) end @@ -274,8 +312,7 @@ class CUnionEntity < CStructEntity # # See Fiddle::Pointer.malloc for memory management issues. def CUnionEntity.malloc(types, func=nil) - addr = Fiddle.malloc(CUnionEntity.size(types)) - CUnionEntity.new(addr, types, func) + super end # Returns the size needed for the union with the given +types+. diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb index 1490fd0b..c2382c0f 100644 --- a/test/fiddle/test_c_struct_entry.rb +++ b/test/fiddle/test_c_struct_entry.rb @@ -43,37 +43,131 @@ def test_class_size_with_count end def test_set_ctypes - struct = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE - struct.assign_names %w[int long] + CStructEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |struct| + struct.assign_names %w[int long] - # this test is roundabout because the stored ctypes are not accessible - struct['long'] = 1 - struct['int'] = 2 + # this test is roundabout because the stored ctypes are not accessible + struct['long'] = 1 + struct['int'] = 2 - assert_equal 1, struct['long'] - assert_equal 2, struct['int'] + assert_equal 1, struct['long'] + assert_equal 2, struct['int'] + end end def test_aref_pointer_array - team = CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE) - team.assign_names(["names"]) - Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice| - alice[0, 6] = "Alice\0" - Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE) do |bob| - bob[0, 4] = "Bob\0" - team["names"] = [alice, bob] - assert_equal(["Alice", "Bob"], team["names"].map(&:to_s)) + CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE) do |team| + team.assign_names(["names"]) + Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice| + alice[0, 6] = "Alice\0" + Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE) do |bob| + bob[0, 4] = "Bob\0" + team["names"] = [alice, bob] + assert_equal(["Alice", "Bob"], team["names"].map(&:to_s)) + end end end end def test_aref_pointer - user = CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE) - user.assign_names(["name"]) - Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice| - alice[0, 6] = "Alice\0" - user["name"] = alice - assert_equal("Alice", user["name"].to_s) + CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE) do |user| + user.assign_names(["name"]) + Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice| + alice[0, 6] = "Alice\0" + user["name"] = alice + assert_equal("Alice", user["name"].to_s) + end + end + end + + def test_new_double_free + types = [TYPE_INT] + Pointer.malloc(CStructEntity.size(types), Fiddle::RUBY_FREE) do |pointer| + assert_raise ArgumentError do + CStructEntity.new(pointer, types, Fiddle::RUBY_FREE) + end + end + end + + def test_new_double_free + types = [TYPE_INT] + Pointer.malloc(CStructEntity.size(types), Fiddle::RUBY_FREE) do |pointer| + assert_raise ArgumentError do + CStructEntity.new(pointer, types, Fiddle::RUBY_FREE) + end + end + end + + def test_malloc_block + escaped_struct = nil + returned = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| + assert_equal Fiddle::SIZEOF_INT, struct.size + assert_equal Fiddle::RUBY_FREE, struct.free.to_i + escaped_struct = struct + :returned + end + assert_equal :returned, returned + assert escaped_struct.freed? + end + + def test_malloc_block_no_free + assert_raise ArgumentError do + CStructEntity.malloc([TYPE_INT]) { |struct| } + end + end + + def test_free + struct = CStructEntity.malloc([TYPE_INT]) + begin + assert_nil struct.free + ensure + Fiddle.free struct + end + end + + def test_free_with_func + struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) + refute struct.freed? + struct.call_free + assert struct.freed? + struct.call_free # you can safely run it again + assert struct.freed? + GC.start # you can safely run the GC routine + assert struct.freed? + end + + def test_free_with_no_func + struct = CStructEntity.malloc([TYPE_INT]) + refute struct.freed? + struct.call_free + refute struct.freed? + struct.call_free # you can safely run it again + refute struct.freed? + end + + def test_freed? + struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) + refute struct.freed? + struct.call_free + assert struct.freed? + end + + def test_null? + struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) + refute struct.null? + end + + def test_size + CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| + assert_equal Fiddle::SIZEOF_INT, struct.size + end + end + + def test_size= + CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| + assert_raise ArgumentError do + struct.size = 1 + end end end end diff --git a/test/fiddle/test_c_union_entity.rb b/test/fiddle/test_c_union_entity.rb index 93100847..e0a37575 100644 --- a/test/fiddle/test_c_union_entity.rb +++ b/test/fiddle/test_c_union_entity.rb @@ -21,15 +21,16 @@ def test_class_size_with_count end def test_set_ctypes - union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE - union.assign_names %w[int long] + CUnionEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |union| + union.assign_names %w[int long] - # this test is roundabout because the stored ctypes are not accessible - union['long'] = 1 - assert_equal 1, union['long'] + # this test is roundabout because the stored ctypes are not accessible + union['long'] = 1 + assert_equal 1, union['long'] - union['int'] = 1 - assert_equal 1, union['int'] + union['int'] = 1 + assert_equal 1, union['int'] + end end end end if defined?(Fiddle) diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index cfa06aa9..ccf8c11c 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -56,22 +56,18 @@ def test_ensure_call_dlload def test_struct_memory_access() # check memory operations performed directly on struct - my_struct = Fiddle::Importer.struct(['int id']).malloc - begin + Fiddle::Importer.struct(['int id']).malloc(Fiddle::RUBY_FREE) do |my_struct| my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT assert_equal 0x01010101, my_struct.id my_struct.id = 0 assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT] - ensure - Fiddle.free my_struct.to_ptr end end def test_struct_ptr_array_subscript_multiarg() # check memory operations performed on struct#to_ptr - struct = Fiddle::Importer.struct([ 'int x' ]).malloc - begin + Fiddle::Importer.struct([ 'int x' ]).malloc(Fiddle::RUBY_FREE) do |struct| ptr = struct.to_ptr struct.x = 0x02020202 @@ -79,33 +75,22 @@ def test_struct_ptr_array_subscript_multiarg() ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT assert_equal 0x01010101, struct.x - ensure - Fiddle.free struct.to_ptr end end def test_malloc() - s1 = LIBC::Timeval.malloc() - begin - s2 = LIBC::Timeval.malloc() - begin + LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |s1| + LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |s2| refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i) - ensure - Fiddle.free s2.to_ptr end - ensure - Fiddle.free s1.to_ptr end end def test_sizeof() assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*")) assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct)) - my_struct = LIBC::MyStruct.malloc() - begin + LIBC::MyStruct.malloc(Fiddle::RUBY_FREE) do |my_struct| assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(my_struct)) - ensure - Fiddle.free my_struct.to_ptr end assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG) end @@ -151,8 +136,7 @@ def test_value() end def test_struct_array_assignment() - instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc - begin + Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc(Fiddle::RUBY_FREE) do |instance| instance.stages[0] = 1024 instance.stages[1] = 10 instance.stages[2] = 100 @@ -163,39 +147,28 @@ def test_struct_array_assignment() instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT] assert_raise(IndexError) { instance.stages[-1] = 5 } assert_raise(IndexError) { instance.stages[3] = 5 } - ensure - Fiddle.free instance.to_ptr end end def test_struct() - s = LIBC::MyStruct.malloc() - begin + LIBC::MyStruct.malloc(Fiddle::RUBY_FREE) do |s| s.num = [0,1,2,3,4] s.c = ?a.ord s.buff = "012345\377" assert_equal([0,1,2,3,4], s.num) assert_equal(?a.ord, s.c) assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff) - ensure - Fiddle.free s.to_ptr end end def test_gettimeofday() if( defined?(LIBC.gettimeofday) ) - timeval = LIBC::Timeval.malloc() - begin - timezone = LIBC::Timezone.malloc() - begin + LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |timeval| + LIBC::Timezone.malloc(Fiddle::RUBY_FREE) do |timezone| LIBC.gettimeofday(timeval, timezone) - ensure - Fiddle.free timezone.to_ptr end cur = Time.now() assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i) - ensure - Fiddle.free timeval.to_ptr end end end From 5cd8f43a5eee1a4f50ac6006a4a64fca35bb8ee1 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 14:52:34 +0100 Subject: [PATCH 04/15] Remove duplicated test --- test/fiddle/test_c_struct_entry.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb index c2382c0f..489ce9a4 100644 --- a/test/fiddle/test_c_struct_entry.rb +++ b/test/fiddle/test_c_struct_entry.rb @@ -89,15 +89,6 @@ def test_new_double_free end end - def test_new_double_free - types = [TYPE_INT] - Pointer.malloc(CStructEntity.size(types), Fiddle::RUBY_FREE) do |pointer| - assert_raise ArgumentError do - CStructEntity.new(pointer, types, Fiddle::RUBY_FREE) - end - end - end - def test_malloc_block escaped_struct = nil returned = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| From fd391c3cd771b5e2087c6ecd02a064f9b98b0a15 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 14:57:19 +0100 Subject: [PATCH 05/15] Make Pointer.malloc allocate an instance of the subclass --- ext/fiddle/pointer.c | 6 +++--- test/fiddle/test_pointer.rb | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c index 1e93a5ab..6a6c97b0 100644 --- a/ext/fiddle/pointer.c +++ b/ext/fiddle/pointer.c @@ -109,13 +109,13 @@ rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func) } static VALUE -rb_fiddle_ptr_malloc(long size, freefunc_t func) +rb_fiddle_ptr_malloc(VALUE klass, long size, freefunc_t func) { void *ptr; ptr = ruby_xmalloc((size_t)size); memset(ptr,0,(size_t)size); - return rb_fiddle_ptr_new(ptr, size, func); + return rb_fiddle_ptr_new2(klass, ptr, size, func); } static void * @@ -268,7 +268,7 @@ rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass) rb_bug("rb_fiddle_ptr_s_malloc"); } - obj = rb_fiddle_ptr_malloc(s,f); + obj = rb_fiddle_ptr_malloc(klass, s,f); if (wrap) RPTR_DATA(obj)->wrap[1] = wrap; if (rb_block_given_p()) { diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb index 865b308d..e685fea5 100644 --- a/test/fiddle/test_pointer.rb +++ b/test/fiddle/test_pointer.rb @@ -50,6 +50,13 @@ def test_malloc_block_no_free end end + def test_malloc_subclass + subclass = Class.new(Pointer) + subclass.malloc(10, Fiddle::RUBY_FREE) do |ptr| + assert ptr.is_a?(subclass) + end + end + def test_to_str str = Marshal.load(Marshal.dump("hello world")) ptr = Pointer[str] From 0c0b26e07c4c3afbd684ba1250ff1749b1f5cdb1 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 15:25:13 +0100 Subject: [PATCH 06/15] Protect against leaking memory in CStructEntity.malloc --- lib/fiddle/struct.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 56d78e4e..dce4a178 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -140,8 +140,8 @@ class CStructEntity < Fiddle::Pointer # # See Fiddle::Pointer.malloc for memory management issues. def CStructEntity.malloc(types, func = nil) - addr = Fiddle.malloc(self.size(types)) - struct = new(addr, types, func) + struct = super(size(types), func, &nil) + struct.set_ctypes types if block_given? raise ArgumentError, 'a free function must be supplied to Fiddle::CStructEntity.malloc when it is called with a block' unless func begin From 25f76a5b7bb2123c54607649e534c7682cb50ef7 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 15:42:22 +0100 Subject: [PATCH 07/15] Clarify shell working for structs and unions --- lib/fiddle/struct.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index dce4a178..ab805465 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -4,7 +4,7 @@ require 'fiddle/pack' module Fiddle - # C struct shell + # A base class for objects representing a C structure class CStruct # accessor to Fiddle::CStructEntity def CStruct.entity_class @@ -12,7 +12,7 @@ def CStruct.entity_class end end - # C union shell + # A base class for objects representing a C union class CUnion # accessor to Fiddle::CUnionEntity def CUnion.entity_class From 9bc85382d2945215a9a6d2c46c583f38c9e3b9ea Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 15:53:48 +0100 Subject: [PATCH 08/15] We can write less code in strings --- lib/fiddle/struct.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index ab805465..4d0ec81d 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -94,7 +94,7 @@ module CStructBuilder def create(klass, types, members) new_class = Class.new(klass){ define_method(:initialize){|addr, func = nil| - @entity = klass.entity_class.new(addr, types, func) + @entity = self.class.entity_class.new(addr, types, func) @entity.assign_names(members) } define_method(:[]) { |*args| @entity.send(:[], *args) } @@ -105,14 +105,8 @@ def create(klass, types, members) define_method(name){ @entity[name] } define_method(name + "="){|val| @entity[name] = val } } - } - size = klass.entity_class.size(types) - new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) - def new_class.size() - #{size} - end - def new_class.malloc(func = nil) - addr = Fiddle.malloc(#{size}) + define_singleton_method(:malloc) do |func=nil| + addr = Fiddle.malloc(size) struct = new(addr, func) if block_given? raise ArgumentError, 'a free function must be supplied to malloc when it is called with a block' unless func @@ -125,6 +119,13 @@ def new_class.malloc(func = nil) struct end end + } + size = klass.entity_class.size(types) + # we use eval here make size into a literal, for performance + new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) + def new_class.size() + #{size} + end EOS return new_class end From 5e9ba4a93a2973be8321235787b6e3fc2b7e3bf6 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 16:15:45 +0100 Subject: [PATCH 09/15] Protect against leaking memory in CStruct subclass malloc --- lib/fiddle/struct.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 4d0ec81d..476c7a12 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -94,7 +94,11 @@ module CStructBuilder def create(klass, types, members) new_class = Class.new(klass){ define_method(:initialize){|addr, func = nil| - @entity = self.class.entity_class.new(addr, types, func) + if addr.is_a?(self.class.entity_class) + @entity = addr + else + @entity = self.class.entity_class.new(addr, types, func) + end @entity.assign_names(members) } define_method(:[]) { |*args| @entity.send(:[], *args) } @@ -106,8 +110,7 @@ def create(klass, types, members) define_method(name + "="){|val| @entity[name] = val } } define_singleton_method(:malloc) do |func=nil| - addr = Fiddle.malloc(size) - struct = new(addr, func) + struct = new(entity_class.malloc(types, func, size)) if block_given? raise ArgumentError, 'a free function must be supplied to malloc when it is called with a block' unless func begin @@ -140,8 +143,8 @@ class CStructEntity < Fiddle::Pointer # Allocates a C struct with the +types+ provided. # # See Fiddle::Pointer.malloc for memory management issues. - def CStructEntity.malloc(types, func = nil) - struct = super(size(types), func, &nil) + def CStructEntity.malloc(types, func = nil, size = size(types), &block) + struct = super(size, func, &nil) struct.set_ctypes types if block_given? raise ArgumentError, 'a free function must be supplied to Fiddle::CStructEntity.malloc when it is called with a block' unless func From c40a7b92153a6c3087fe31d525f40dc974d62436 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 22:29:46 +0100 Subject: [PATCH 10/15] Update lib/fiddle/struct.rb Co-authored-by: Sutou Kouhei --- lib/fiddle/struct.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 476c7a12..7e6db0db 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -144,16 +144,14 @@ class CStructEntity < Fiddle::Pointer # # See Fiddle::Pointer.malloc for memory management issues. def CStructEntity.malloc(types, func = nil, size = size(types), &block) - struct = super(size, func, &nil) - struct.set_ctypes types if block_given? - raise ArgumentError, 'a free function must be supplied to Fiddle::CStructEntity.malloc when it is called with a block' unless func - begin + super(size, func) do |struct| + struct.set_ctypes types yield struct - ensure - struct.call_free end else + struct = super(size, func) + struct.set_ctypes types struct end end @@ -341,4 +339,3 @@ def set_ctypes(types) end end end - From 2956f6439d7e128a8affcaf335d5f2c79de9f717 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 22:30:08 +0100 Subject: [PATCH 11/15] Update lib/fiddle/struct.rb Co-authored-by: Sutou Kouhei --- lib/fiddle/struct.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 7e6db0db..b565b363 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -110,16 +110,12 @@ def create(klass, types, members) define_method(name + "="){|val| @entity[name] = val } } define_singleton_method(:malloc) do |func=nil| - struct = new(entity_class.malloc(types, func, size)) if block_given? - raise ArgumentError, 'a free function must be supplied to malloc when it is called with a block' unless func - begin - yield struct - ensure - struct.to_ptr.call_free + entity_class.malloc(types, func, size) do |entity| + yield new(entity) end else - struct + new(entity_class.malloc(types, func, size)) end end } From 05bca8594c9fa51584ca0aa71cf2c93c198f5132 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 22:34:48 +0100 Subject: [PATCH 12/15] Update lib/fiddle/struct.rb Co-authored-by: Sutou Kouhei --- lib/fiddle/struct.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index b565b363..a518017d 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -293,10 +293,7 @@ def []=(*args) end end - def size=(new_size) # :nodoc: - raise ArgumentError, 'cannot change the size of a struct' - end - + undef_method :size= def to_s() # :nodoc: super(@size) end From 4e48e5b0eace804dd51bdd9ea04cc35434224342 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 22:28:04 +0100 Subject: [PATCH 13/15] Don't eval methods in struct --- lib/fiddle/struct.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index a518017d..401a1709 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -109,6 +109,8 @@ def create(klass, types, members) define_method(name){ @entity[name] } define_method(name + "="){|val| @entity[name] = val } } + size = klass.entity_class.size(types) + define_singleton_method(:size) { size } define_singleton_method(:malloc) do |func=nil| if block_given? entity_class.malloc(types, func, size) do |entity| @@ -119,13 +121,6 @@ def create(klass, types, members) end end } - size = klass.entity_class.size(types) - # we use eval here make size into a literal, for performance - new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) - def new_class.size() - #{size} - end - EOS return new_class end module_function :create From bf73fdc8a816bdbfd215b0fe4fd1853978ccdf6f Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 22:31:00 +0100 Subject: [PATCH 14/15] Remove CUnionEntity.malloc --- lib/fiddle/struct.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 401a1709..318e8314 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -298,13 +298,6 @@ def to_s() # :nodoc: class CUnionEntity < CStructEntity include PackInfo - # Allocates a C union the +types+ provided. - # - # See Fiddle::Pointer.malloc for memory management issues. - def CUnionEntity.malloc(types, func=nil) - super - end - # Returns the size needed for the union with the given +types+. # # Fiddle::CUnionEntity.size( From 9647e3cedf6df7f9d28f06fe055cd7fa305e6267 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Sun, 21 Jun 2020 22:35:59 +0100 Subject: [PATCH 15/15] Fix CStructEntity#size= spec --- test/fiddle/test_c_struct_entry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb index 489ce9a4..9fd16d71 100644 --- a/test/fiddle/test_c_struct_entry.rb +++ b/test/fiddle/test_c_struct_entry.rb @@ -156,7 +156,7 @@ def test_size def test_size= CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| - assert_raise ArgumentError do + assert_raise NoMethodError do struct.size = 1 end end