Subject: [ruby-ffi] Re: using :buffer_out with strings in ruby vs jruby |
From: Aman Gupta |
Date: 9/14/09 2:23 PM |
To: ruby-ffi@googlegroups.com |
Thanks, changing it to :buffer_in works as expected. When I create an explicit Buffer and pass it in using :pointer, the memory allocated for that buffer is freed when the ruby object is garbage collected. What happens when I pass in a string as :buffer_in instead? The contents of the string are copied into a temporary buffer, but when is the buffer freed? or does it live on the stack? Aman On Mon, Sep 14, 2009 at 7:25 AM, Wayne Meissner <wmeissner@gmail.com> wrote:
<themastermind1@gmail.com>:If that code is doing what I think it is doing, you have the direction wrong. :buffer_out == copy out from native memory to ruby memory :buffer_in == copy in to native memory from ruby memory Think of them like IN and OUT parameter specifications. IN means the data is passed IN to the function, OUT means the function can alter the value of the parameter. INOUT means both. As to why the tests fail if you pass the string first, its because the string gets overwritten with garbage from native memory due to the OUT param spec. The tests work with :pointer, because it is basically the same as :buffer_inout when it comes to string and Buffer arguments - the data is first copied IN to native memory from ruby, then after the call, the native data is copied OUT from native memory to ruby, overwriting the data with the same thing as was already there. 2009/9/14 Aman GuptaI'm seeing different behavior on ruby vs jruby when passing in a string as a :buffer_out argument: require 'rubygems' require 'ffi' module GLib extend FFI::Library ffi_lib 'libglib-2.0' attach_function :g_memdup_buf, :g_memdup, [ :buffer_out, :uint ], :pointer attach_function :g_memdup_ptr, :g_memdup, [ :pointer, :uint ], :pointer def self.test_memdup(arg_type, func_type) ostr = "a\0b\0c" if arg_type == :string arg = ostr.dup elsif arg_type == :buffer arg = FFI::Buffer.new(ostr.size) arg.put_bytes(0, ostr) end if func_type == :buffer_out ret = GLib.g_memdup_buf(arg, arg.size) elsif func_type == :pointer ret = GLib.g_memdup_ptr(arg, arg.size) end rstr = ret.get_bytes(0, ostr.size) p [arg_type, func_type, ostr, rstr, ostr == rstr] end end GLib.test_memdup(:buffer, :buffer_out) GLib.test_memdup(:buffer, :pointer) GLib.test_memdup(:string, :buffer_out) GLib.test_memdup(:string, :pointer) $ ruby glib_memduptest.rb [:buffer, :buffer_out, "a\000b\000c", "a\000b\000c", true] [:buffer, :pointer, "a\000b\000c", "a\000b\000c", true] [:string, :buffer_out, "a\000b\000c", "a\000b\000c", true] [:string, :pointer, "a\000b\000c", "a\000b\000c", true] $ jruby glib_memduptest.rb [:buffer, :buffer_out, "a\000b\000c", "\300\370\177\260A", false] [:buffer, :pointer, "a\000b\000c", "a\000b\000c", true] [:string, :buffer_out, "\001\000\000\000\270", "\001\000\000\000\270", true] [:string, :pointer, "\001\000\000\000\270", "\001\000\000\000\270", true] I'm not really sure what's happening in the jruby case. If I use an explicit FFI::Buffer with :pointer, it seems to work. What's really strange though, is that if you reverse the order in which the tests happen (so the string versions execute first), they all fail: GLib.test_memdup(:string, :buffer_out) GLib.test_memdup(:string, :pointer) GLib.test_memdup(:buffer, :buffer_out) GLib.test_memdup(:buffer, :pointer) $ jruby glib_memduptest.rb [:string, :buffer_out, "\300\370\177\260A", "\300\370\177\260A", true] [:string, :pointer, "\300\370\177\260A", "\300\370\177\260A", true] [:buffer, :buffer_out, "\300\370\177\260A", "0\f\200\001@", false] [:buffer, :pointer, "\300\370\177\260A", "\300\370\177\260A", true]