From b83ad9049b5b7c591f757add32e94a6e5478f7c1 Mon Sep 17 00:00:00 2001 From: Colin MacKenzie IV Date: Tue, 21 Jan 2020 21:30:00 -0500 Subject: [PATCH 1/7] Allow access to a struct's underlying memory with `struct[offset, length]`. # Conflicts: # test/fiddle/test_import.rb --- lib/fiddle/struct.rb | 8 ++++++-- test/fiddle/test_import.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 7c0dedb3..8330b01d 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -149,7 +149,9 @@ def set_ctypes(types) end # Fetch struct member +name+ - def [](name) + def [](*args) + return super(*args) if args.size > 1 + name = args[0] idx = @members.index(name) if( idx.nil? ) raise(ArgumentError, "no such member: #{name}") @@ -183,7 +185,9 @@ def [](name) end # Set struct member +name+, to value +val+ - def []=(name, val) + def []=(*args) + return super(*args) if args.size > 2 + name, val = *args idx = @members.index(name) if( idx.nil? ) raise(ArgumentError, "no such member: #{name}") diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 3170e9e4..5ad074e8 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -107,6 +107,14 @@ def test_value() assert_equal([0,1,2], ary.value) end + def test_struct_array_subscript_multiarg() + struct = Fiddle::Importer.struct([ 'int x' ]).malloc + assert_equal("\x00".b * Fiddle::SIZEOF_INT, struct.to_ptr[0, Fiddle::SIZEOF_INT]) + + struct.to_ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT + assert_equal 16843009, struct.x + end + def test_struct() s = LIBC::MyStruct.malloc() s.num = [0,1,2,3,4] From d602fa0c9320e638ceb5fa1f45f3ec90b82653c9 Mon Sep 17 00:00:00 2001 From: Colin MacKenzie IV Date: Mon, 29 Oct 2018 21:44:01 -0400 Subject: [PATCH 2/7] Make accessing a struct's underlying memory more convenient. --- lib/fiddle/struct.rb | 27 +++++++++++++++++++++++++-- test/fiddle/test_import.rb | 10 ++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 8330b01d..2c261b3f 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -54,6 +54,8 @@ def create(klass, types, members) @entity = klass.entity_class.new(addr, types) @entity.assign_names(members) } + define_method(:[]) { |*args| @entity.send(:[], *args) } + define_method(:[]=) { |*args| @entity.send(:[]=, *args) } define_method(:to_ptr){ @entity } define_method(:to_i){ @entity.to_i } members.each{|name| @@ -148,7 +150,18 @@ def set_ctypes(types) @size = PackInfo.align(offset, max_align) end - # Fetch struct member +name+ + # Fetch struct member +name+ if only one argument is specified. If two + # arguments are specified, the first is an offset and the second is a + # length and this method returns the string of +length+ bytes beginning at + # +offset+. + # + # Examples: + # + # my_struct = struct(['int id']).malloc + # my_struct.id = 1 + # my_struct['id'] # => 1 + # my_struct[0, 4] # => "\x01\x00\x00\x00".b + # def [](*args) return super(*args) if args.size > 1 name = args[0] @@ -184,7 +197,17 @@ def [](*args) end end - # Set struct member +name+, to value +val+ + # Set struct member +name+, to value +val+. If more arguments are + # specified, writes the string of bytes to the memory at the given + # +offset+ and +length+. + # + # Examples: + # + # my_struct = struct(['int id']).malloc + # my_struct['id'] = 1 + # my_struct[0, 4] = "\x01\x00\x00\x00".b + # my_struct.id # => 1 + # def []=(*args) return super(*args) if args.size > 2 name, val = *args diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 5ad074e8..ed3503cd 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -54,6 +54,16 @@ def test_ensure_call_dlload assert_match(/call dlload before/, err.message) end + def test_struct_memory_access + my_struct = Fiddle::Importer.struct(['int id']).malloc + my_struct['id'] = 1 + my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT + refute_equal 0, my_struct.id + + my_struct.id = 0 + assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT] + end + def test_malloc() s1 = LIBC::Timeval.malloc() s2 = LIBC::Timeval.malloc() From 28a01a618368ab75ef52900b232222c325b3ef4e Mon Sep 17 00:00:00 2001 From: Colin MacKenzie IV Date: Tue, 21 Jan 2020 22:17:37 -0500 Subject: [PATCH 3/7] refactor memory access unit tests for improved clarity --- test/fiddle/test_import.rb | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index ed3503cd..66414d7c 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -54,16 +54,28 @@ def test_ensure_call_dlload assert_match(/call dlload before/, err.message) end - def test_struct_memory_access + def test_struct_memory_access() + # check memory operations performed directly on struct my_struct = Fiddle::Importer.struct(['int id']).malloc - my_struct['id'] = 1 my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT - refute_equal 0, my_struct.id + assert_equal 0x01010101, my_struct.id my_struct.id = 0 assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT] end + def test_struct_ptr_array_subscript_multiarg() + # check memory operations performed on struct#to_ptr + struct = Fiddle::Importer.struct([ 'int x' ]).malloc + ptr = struct.to_ptr + + struct.x = 0x02020202 + assert_equal("\x02".b * Fiddle::SIZEOF_INT, ptr[0, Fiddle::SIZEOF_INT]) + + ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT + assert_equal 0x01010101, struct.x + end + def test_malloc() s1 = LIBC::Timeval.malloc() s2 = LIBC::Timeval.malloc() @@ -117,14 +129,6 @@ def test_value() assert_equal([0,1,2], ary.value) end - def test_struct_array_subscript_multiarg() - struct = Fiddle::Importer.struct([ 'int x' ]).malloc - assert_equal("\x00".b * Fiddle::SIZEOF_INT, struct.to_ptr[0, Fiddle::SIZEOF_INT]) - - struct.to_ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT - assert_equal 16843009, struct.x - end - def test_struct() s = LIBC::MyStruct.malloc() s.num = [0,1,2,3,4] From 1dbc26f315c0a2a326272491f6d6b105c2f00b5d Mon Sep 17 00:00:00 2001 From: Colin MacKenzie IV Date: Tue, 21 Jan 2020 21:32:51 -0500 Subject: [PATCH 4/7] fix assignment to elements in a member which is an array --- lib/fiddle/struct.rb | 29 ++++++++++++++++++++++++++++- test/fiddle/test_import.rb | 10 ++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 2c261b3f..4d8d3e42 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -20,6 +20,33 @@ def CUnion.entity_class end end + # Wrapper for arrays within a struct + class StructArray < Array + include ValueUtil + + def initialize(ptr, type, initial_values) + @ptr = ptr + @type = type + @align = PackInfo::ALIGN_MAP[type] + @size = Fiddle::PackInfo::SIZE_MAP[type] + @pack_format = Fiddle::PackInfo::PACK_MAP[type] + super(initial_values.collect { |v| unsigned_value(v, type) }) + end + + def to_ptr + @ptr + end + + def []=(index, value) + if index < 0 || index >= size + raise RangeError, 'index %d outside of array bounds 0...%d' % [index, size] + end + + to_ptr[index * @align, @size] = [value].pack(@pack_format) + super(index, value) + end + end + # Used to construct C classes (CUnion, CStruct, etc) # # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an @@ -191,7 +218,7 @@ def [](*args) if( ty.is_a?(Integer) && (ty < 0) ) return unsigned_value(val, ty) elsif( ty.is_a?(Array) && (ty[0] < 0) ) - return val.collect{|v| unsigned_value(v,ty[0])} + return StructArray.new(self + @offset[idx], ty[0], val) else return val end diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 66414d7c..484f7513 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -129,6 +129,16 @@ def test_value() assert_equal([0,1,2], ary.value) end + def test_struct_array_assignment() + instance = Fiddle::Importer.struct(["unsigned int stages[1]"]).malloc + instance.stages[0] = 1024 + assert_equal 1024, instance.stages[0] + assert_equal [1024].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT]), + instance.to_ptr[0, Fiddle::SIZEOF_INT] + assert_raise(RangeError) { instance.stages[-1] = 5 } + assert_raise(RangeError) { instance.stages[2] = 5 } + end + def test_struct() s = LIBC::MyStruct.malloc() s.num = [0,1,2,3,4] From 5869786dd3ec94e4630f7c4586ec7d9da7abaca8 Mon Sep 17 00:00:00 2001 From: Colin MacKenzie IV Date: Wed, 22 Jan 2020 12:49:13 -0500 Subject: [PATCH 5/7] use IndexError instead of RangeError --- lib/fiddle/struct.rb | 2 +- test/fiddle/test_import.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 4d8d3e42..12693df1 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -39,7 +39,7 @@ def to_ptr def []=(index, value) if index < 0 || index >= size - raise RangeError, 'index %d outside of array bounds 0...%d' % [index, size] + raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size] end to_ptr[index * @align, @size] = [value].pack(@pack_format) diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 484f7513..897119e3 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -135,8 +135,8 @@ def test_struct_array_assignment() assert_equal 1024, instance.stages[0] assert_equal [1024].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT]), instance.to_ptr[0, Fiddle::SIZEOF_INT] - assert_raise(RangeError) { instance.stages[-1] = 5 } - assert_raise(RangeError) { instance.stages[2] = 5 } + assert_raise(IndexError) { instance.stages[-1] = 5 } + assert_raise(IndexError) { instance.stages[2] = 5 } end def test_struct() From fc02e12b776623ded785d521f099f88bc504ee17 Mon Sep 17 00:00:00 2001 From: Colin MacKenzie IV Date: Wed, 22 Jan 2020 12:50:07 -0500 Subject: [PATCH 6/7] use @size, not @align --- lib/fiddle/struct.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fiddle/struct.rb b/lib/fiddle/struct.rb index 12693df1..ce37eb43 100644 --- a/lib/fiddle/struct.rb +++ b/lib/fiddle/struct.rb @@ -42,7 +42,7 @@ def []=(index, value) raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size] end - to_ptr[index * @align, @size] = [value].pack(@pack_format) + to_ptr[index * @size, @size] = [value].pack(@pack_format) super(index, value) end end From 28ab0e345d520a707e01dbac4320e84d9d4e71af Mon Sep 17 00:00:00 2001 From: Colin MacKenzie IV Date: Wed, 22 Jan 2020 13:12:08 -0500 Subject: [PATCH 7/7] more testing for array access with indices other than 0 --- test/fiddle/test_import.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 897119e3..124fffba 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -130,13 +130,17 @@ def test_value() end def test_struct_array_assignment() - instance = Fiddle::Importer.struct(["unsigned int stages[1]"]).malloc + instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc instance.stages[0] = 1024 + instance.stages[1] = 10 + instance.stages[2] = 100 assert_equal 1024, instance.stages[0] - assert_equal [1024].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT]), - instance.to_ptr[0, Fiddle::SIZEOF_INT] + assert_equal 10, instance.stages[1] + assert_equal 100, instance.stages[2] + assert_equal [1024, 10, 100].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT] * 3), + instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT] assert_raise(IndexError) { instance.stages[-1] = 5 } - assert_raise(IndexError) { instance.stages[2] = 5 } + assert_raise(IndexError) { instance.stages[3] = 5 } end def test_struct()