diff options
| author | Aaron Patterson <tenderlove@ruby-lang.org> | 2023-03-16 08:52:40 -0700 |
|---|---|---|
| committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2023-06-28 16:36:49 +0900 |
| commit | 85937f3a0afeb7a2b3f9b8cb0dcaa8a20c49e09d (patch) | |
| tree | 38aa4173a2ae1036d3ed0317eb288d6ffc6276d2 | |
| parent | ae71bbbc44e9389e229c039d4717b1e7bcf63631 (diff) | |
[ruby/fiddle] Add a helper method for reading/writing memory
(https://github.com/ruby/fiddle/pull/123)
This commit adds two new methods, `Fiddle::Pointer.read` and
`Fiddle::Pointer.write`. Both methods take an address, and will read or
write bytes at that address respectively.
For example we can read from an address without making a Pointer object:
```ruby
Fiddle::Pointer.read(address, 5) # read 5 bytes
```
We can also write to an address without allocating a Pointer object:
```ruby
Fiddle::Pointer.write(address, "bytes") # write 5 bytes
```
This allows us to read / write memory at arbitrary addresses without
instantiating a new `Fiddle::Pointer` object.
Examples where this API would be useful
[1](https://github.com/tenderlove/tenderjit/blob/f03481d28bff4d248746e596929b0841de65f181/lib/tenderjit/fiddle_hacks.rb#L26-L28)
[2](https://github.com/tenderlove/ruby/blob/77c8daa2d40dd58eeb3785ce17dea2ee38f308d1/lib/ruby_vm/rjit/c_pointer.rb#L193)
[3](https://github.com/tenderlove/ruby/blob/77c8daa2d40dd58eeb3785ce17dea2ee38f308d1/lib/ruby_vm/rjit/c_pointer.rb#L284)
I also added a writer method for the same reasons as the reader.
---------
https://github.com/ruby/fiddle/commit/04238cefed
Co-authored-by: Sutou Kouhei <kou@clear-code.com>
| -rw-r--r-- | ext/fiddle/pointer.c | 29 | ||||
| -rw-r--r-- | test/fiddle/test_pointer.rb | 16 |
2 files changed, 45 insertions, 0 deletions
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c index 15107e3862..5c71967ab4 100644 --- a/ext/fiddle/pointer.c +++ b/ext/fiddle/pointer.c @@ -799,6 +799,33 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) return ptr; } +/* + * call-seq: + * Fiddle::Pointer.read(address, len) => string + * + * Or read the memory at address +address+ with length +len+ and return a + * string with that memory + */ + +static VALUE +rb_fiddle_ptr_read_mem(VALUE klass, VALUE address, VALUE len) +{ + return rb_str_new((char *)NUM2PTR(address), NUM2ULONG(len)); +} + +/* + * call-seq: + * Fiddle::Pointer.write(address, str) + * + * Write bytes in +str+ to the location pointed to by +address+. + */ +static VALUE +rb_fiddle_ptr_write_mem(VALUE klass, VALUE addr, VALUE str) +{ + memcpy(NUM2PTR(addr), StringValuePtr(str), RSTRING_LEN(str)); + return str; +} + void Init_fiddle_pointer(void) { @@ -815,6 +842,8 @@ Init_fiddle_pointer(void) rb_define_singleton_method(rb_cPointer, "malloc", rb_fiddle_ptr_s_malloc, -1); rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1); rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1); + rb_define_singleton_method(rb_cPointer, "read", rb_fiddle_ptr_read_mem, 2); + rb_define_singleton_method(rb_cPointer, "write", rb_fiddle_ptr_write_mem, 2); rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1); rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1); rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0); diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb index d6cba04bbe..f2c1d285ad 100644 --- a/test/fiddle/test_pointer.rb +++ b/test/fiddle/test_pointer.rb @@ -10,6 +10,22 @@ module Fiddle Fiddle.dlwrap arg end + def test_can_read_write_memory + # Allocate some memory + address = Fiddle.malloc(Fiddle::SIZEOF_VOIDP) + + bytes_to_write = Fiddle::SIZEOF_VOIDP.times.to_a.pack("C*") + + # Write to the memory + Fiddle::Pointer.write(address, bytes_to_write) + + # Read the bytes out again + bytes = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP) + assert_equal bytes_to_write, bytes + ensure + Fiddle.free address + end + def test_cptr_to_int null = Fiddle::NULL assert_equal(null.to_i, null.to_int) |
