Traditionally with pointers you’ll have a method, like attach_function :GetForegroundWindow, [ ], :pointer
What this returns to you is a pointer that, probably, some other library is managing. You don’t need to release this pointer or anything.
To create your own pointer, do something like ptr = MemoryPointer.new(4) 4 bytes worth of memory
It will be freed when the pointer object is GC’ed or you call ptr.free
.
ptr.address
is the address it points at. To copy some other pointer, it’s another_ptr = ptr
A pointer is merely a Fixnum that holds a native memory address. Think of “Pointer” as “fixnum with methods to read/write the native memory at the address”.
Handing off memory to external libraries
Some situations will require allocating native memory and handing off that buffer to an external library. The external library then handles the lifecycle of that buffer including eventually freeing it.
Wrap libc and use its malloc()
and free()
functions to allocate and free native memory.
module LibC
extend FFI::Library
ffi_lib FFI::Library::LIBC
# memory allocators
attach_function :malloc, [:size_t], :pointer
attach_function :calloc, [:size_t], :pointer
attach_function :valloc, [:size_t], :pointer
attach_function :realloc, [:pointer, :size_t], :pointer
attach_function :free, [:pointer], :void
# memory movers
attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
attach_function :bcopy, [:pointer, :pointer, :size_t], :void
end # module LibC
In the Ruby code, calls to these functions will return instances of FFI::Pointer
. Use the methods defined on FFI::Pointer
to move data from Ruby memory to native memory.
foo = "a Ruby string"
= 3.14159
baz = [1, 2, 3, 4, 5]
buffer1 = LibC.malloc foo.size
buffer1.write_string foo
buffer2 = LibC.malloc .size
buffer2.write_float
# all of the array elements need to be the same type
# meaning you can't mix ints, floats, strings, etc.
buffer3 = LibC.malloc(baz.first.size * baz.size)
buffer3.write_array_of_int baz
MemoryPointer
The FFI::MemoryPointer
class allocates native memory with automatic garbage collection as a sweetener. When a MemoryPointer goes out of scope, the memory is freed up as part of the garbage collection process.
The MemoryPointer constructor takes 3 arguments: size
, count
and clear
. The size
argument is a symbol type that determines the number of bytes to allocate. The size
argument can also be any object that responds to #size
in which case it will allocate that specific number of bytes. The count
argument is a multiplier for size; it will allocate size * count
bytes of memory. Lastly, the clear
argument tells the memory allocator to zero/initialize the memory when true, skip initialization when false.
The block form of MemoryPointer is also useful for automatically freeing the pointer when finished, as follows:
baz = [ 5, 2, 88, 92, 32, 1291 ]
FFI::MemoryPointer.new(:int, baz.size) do |p|
p.write_array_of_int(baz)
C.DoSomethingWithArrayOfInt(p)
end
String pointer
a pointer into a Ruby string:
FFI::MemoryPointer.from_string('some string')
Fresh Memory
Some C libraries will return you a pointer to “fresh” memory. That means you’re responsible for freeing that memory after you’re finished with it. Such a library (“foo”) might have the following interface:
// Encodes data of length data_size. The caller is responsible for destroying
// the return value using foo_free() when finished with it.
char * foo_encode (const byte *data, size_t data_size);
// Frees a pointer returned by foo_encode().
void foo_free (char * data);
So foo_encode()
takes a pointer to some data, and a length of that data. It’ll do s
A very basic way to release the memory after being done with it this is:
module Foo
# Module with native functions of libfoo.
module FFI
extend FFI::Library
ffi_lib 'libfoo'
attach_function :foo_encode, [:pointer, :size_t], :pointer # returns fresh memory
attach_function :foo_free, [ :pointer ], :void # frees memory
end
end
# #...
begin
p_input = FFI::MemoryPointer.from_string("input")
p_out = Foo::FFI.foo_encode(p_input, p_input.size)
# with p_out, we can:
# * get a Ruby string from it, when needed:
# puts p_out.read_string
# * pass it to another C function
# * use it to free the memory
ensure # make sure this block is called, no matter what
# free it, if p_out still holds a pointer
Foo::FFI.free(p_out) if p_out
end
Pretty straight-forward, but not very clean. We’re explicitly freeing the memory here, and we had to put it in an ensure
block to be sure it’ll be freed. But it’s a bit cumbersome to do it this way. In Ruby, we’re not used to worry about these things, because there’s the Garbage Collector.
What if we want to keep the pointer around for longer, maybe to pass it to another C function later (outside of this block)? We want to be sure that the memory eventually gets freed, to avoid a memory leak. This should happen when our reference to it (like p_out
) goes out of scope, because that’s the point where we won’t have access to that memory anymore.
To do this, we can wrap our pointer in an instance of FFI::AutoPointer
along with our custom release strategy as a Method
object:
module Foo
# Module with native functions of libfoo.
module FFI
extend FFI::Library
ffi_lib 'libfoo'
attach_function :foo_encode, [:pointer, :size_t], :pointer # returns fresh memory
attach_function :foo_free, [ :pointer ], :void # frees memory
end
end
# #...
begin
p_input = FFI::MemoryPointer.from_string("input")
# wrap result in a Foo::FFI::AutoPointer
p_out = FFI::AutoPointer.new(Foo::FFI.foo_encode(p_input, p_input.size),
Foo::FFI.method(:foo_free))
# #...
end
# p_input is out of scope here, so it gets GC'd soon #...
As you can see, we’re not explicitly freeing the pointer here. There’s no need to manually free it here. It’ll be freed as soon as p_input goes out of scope and is GC’d.
As an alternative, we could have inherited from FFI::AutoPointer
and specified our custom release strategy in its release
class method:
module Foo
module FFI
# Used to automatically free memory using the foo_free() function.
class AutoPointer < ::FFI::AutoPointer
# This method will be called by FFI::AutoPointer::DefaultReleaser.
def self.release(ptr)
FFI.foo_free(ptr)
end
end
end
end
# #...
p_out = Foo::FFI::AutoPointer.new(p_out)
Whatever suits your needs. But providing a Method
object is the preferred way.
Fresh Strings
If you want to access the memory as a String (every time), you can use :strptr
as a convenience:
module Foo
# Module with native functions of libfoo.
module FFI
extend FFI::Library
ffi_lib 'libfoo'
attach_function :foo_encode, [:pointer, :size_t], :strptr # return String AND Pointer
attach_function :foo_free, [ :pointer ], :void
end
end
# #...
p_input = FFI::MemoryPointer.from_string("input")
# call foo_encode() and get a String and a Pointer back
string_out, p_out = Foo::FFI.foo_encode(p_input, p_input.size)
# wrap pointer in a Foo::FFI::AutoPointer
p_out = FFI::AutoPointer.new(p_out, Foo::FFI.method(:foo_free))
puts "encoded string: #{string_out}"
# #...
This way, you don’t have to call #read_string
before having access to the Ruby string.
Beware that the returned String might have the wrong encoding set. Use String#force_encoding
. See [[Binary Data]] as well.
Passing by reference
Since C can only return 1 value, most C APIs take pointers to output locations which they will write to. For an example, let’s call the C-function: ```C void set_value(int x) { *x = (x + 1) % 31337; }
Since this function treats int *x
as equivalent to int x[1]
, this situation can be handled by defining a 1-element array (as in the last section) or, alternately by defining a 1-element structure,
class IntPtr < FFI::Struct
layout :value, :int
end
module ValueSetter
extend FFI::Library
ffi_lib '/usr/local/lib/set_value.so'
attach_function :set_value, [IntPtr], :void
end
xptr = IntPtr.new
xptr[:value] = 42
ValueSetter::set_value(xptr)
puts xptr[:value]