Skip to content

Commit c082c81

Browse files
sinisterchipmunknobu
authored andcommitted
Make array access override compatible with base class (#25)
* Allow access to a struct's underlying memory with `struct[offset, length]`. * Make accessing a struct's underlying memory more convenient. * refactor memory access unit tests for improved clarity
1 parent 8414239 commit c082c81

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

lib/fiddle/struct.rb

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def create(klass, types, members)
5454
@entity = klass.entity_class.new(addr, types)
5555
@entity.assign_names(members)
5656
}
57+
define_method(:[]) { |*args| @entity.send(:[], *args) }
58+
define_method(:[]=) { |*args| @entity.send(:[]=, *args) }
5759
define_method(:to_ptr){ @entity }
5860
define_method(:to_i){ @entity.to_i }
5961
members.each{|name|
@@ -148,8 +150,21 @@ def set_ctypes(types)
148150
@size = PackInfo.align(offset, max_align)
149151
end
150152

151-
# Fetch struct member +name+
152-
def [](name)
153+
# Fetch struct member +name+ if only one argument is specified. If two
154+
# arguments are specified, the first is an offset and the second is a
155+
# length and this method returns the string of +length+ bytes beginning at
156+
# +offset+.
157+
#
158+
# Examples:
159+
#
160+
# my_struct = struct(['int id']).malloc
161+
# my_struct.id = 1
162+
# my_struct['id'] # => 1
163+
# my_struct[0, 4] # => "\x01\x00\x00\x00".b
164+
#
165+
def [](*args)
166+
return super(*args) if args.size > 1
167+
name = args[0]
153168
idx = @members.index(name)
154169
if( idx.nil? )
155170
raise(ArgumentError, "no such member: #{name}")
@@ -182,8 +197,20 @@ def [](name)
182197
end
183198
end
184199

185-
# Set struct member +name+, to value +val+
186-
def []=(name, val)
200+
# Set struct member +name+, to value +val+. If more arguments are
201+
# specified, writes the string of bytes to the memory at the given
202+
# +offset+ and +length+.
203+
#
204+
# Examples:
205+
#
206+
# my_struct = struct(['int id']).malloc
207+
# my_struct['id'] = 1
208+
# my_struct[0, 4] = "\x01\x00\x00\x00".b
209+
# my_struct.id # => 1
210+
#
211+
def []=(*args)
212+
return super(*args) if args.size > 2
213+
name, val = *args
187214
idx = @members.index(name)
188215
if( idx.nil? )
189216
raise(ArgumentError, "no such member: #{name}")

test/fiddle/test_import.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,28 @@ def test_ensure_call_dlload
5454
assert_match(/call dlload before/, err.message)
5555
end
5656

57+
def test_struct_memory_access()
58+
# check memory operations performed directly on struct
59+
my_struct = Fiddle::Importer.struct(['int id']).malloc
60+
my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
61+
assert_equal 0x01010101, my_struct.id
62+
63+
my_struct.id = 0
64+
assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
65+
end
66+
67+
def test_struct_ptr_array_subscript_multiarg()
68+
# check memory operations performed on struct#to_ptr
69+
struct = Fiddle::Importer.struct([ 'int x' ]).malloc
70+
ptr = struct.to_ptr
71+
72+
struct.x = 0x02020202
73+
assert_equal("\x02".b * Fiddle::SIZEOF_INT, ptr[0, Fiddle::SIZEOF_INT])
74+
75+
ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
76+
assert_equal 0x01010101, struct.x
77+
end
78+
5779
def test_malloc()
5880
s1 = LIBC::Timeval.malloc()
5981
s2 = LIBC::Timeval.malloc()

0 commit comments

Comments
 (0)