How to use pointers to Ruby objects safely from within C-based extension?
I am considering writing a C-based Ruby gem to speed up text wrapping in Prawn. I've read a very small portion of the C source for MRI before, but don't know the API for building extensions well yet.
Within my C code, I'd like to get a direct pointer to the data within a Ruby String, and walk over it byte by byte. Further to that, I'd like to store pointers within the buffer in my own struct, and use them not only within the scope of a single call, but within subsequent calls into the extension code.
Is this possible? Could the GC move the Strings, rendering my pointers invalid? And also, how can I let Ruby know that I am holding pointers to the Strings in my own structs (so the GC doesn't try to reclaim them)? Can this code be written in a way which is compatible with both MRI 1.8 and 1.9?
And since I'm asking about using pointers safely in a C-based Ruby extension: can I use malloc and free the same as I would in a "regular" C-based project?
The link that matt offers is really good. It would have saved me days if I had found it before.
You can keep references to ruby Strings and pointers into them. I would suggest freezing the String. Then every attempt to change the string will fail. There is a function Data_Wrap_Struct() that lets you wrap your own data structure into a Ruby object. Beside the data structure and the class of the structure, the function takes two function arguments. One of them (mark) is used to show the garbage collector where your structure references other ruby objects.
What took me some time to understand, is that the garbage collector is really scanning the stack of all ruby threads to seek for references to ruby objects. So keeping VALUEs on the stack is also a safe method to keep objects referenced.
Can this code be written in a way which is compatible with both MRI 1.8 and 1.9?
The basic API for extensions didn't change very much (I think) from 1.8 to 1.9. But I've used only 1.9 so far.
can I use malloc and free the same as I would in a "regular" C-based project?
Sure, I cannot think of any reason why this should not possible, as long as you don't expect the garbage collector to keep care of the allocated memory.
I had a hard time, mixing C++ code, compiled with another version of gcc than the version the ruby interpreter was compiled with. If you experience strange startup behavior, I would check for compiler version differences.