Subject:
[ruby-ffi] Re: using :buffer_out with strings in ruby vs jruby
From:
Wayne Meissner
Date:
9/14/09 9:25 AM
To:
ruby-ffi@googlegroups.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 Gupta <themastermind1@gmail.com>:
>
> I'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]
>