[Ruby C] Using DATA_PTR in multiple methods

I’m writing a Ruby extension in C to utilize a library (tsk3, aka. The
Sleuth Kit) for opening disk images, and I found I couldn’t get
Data_Get_Struct to return the Struct built during initialization.
This seemed odd as I built my class “by the book”, and could get data
from inside the Struct during initialization.

The only workaround I found was referring to DATA_PTR directly (see code
below), which is odd because I found few examples of this in the
community. I thought the whole point of Data_Get_Struct(self, C TYPE,
ptr) was to fetch the Struct wrapped by ‘self’ and return a reference
(ptr) to it. I’m developing on 1.8.7, but will be moving on to 1.9
soon, and would like to know if I’m about to make a major mistake by
using what looks like an old workaround. Why would VALUE self not be
sufficient for Ruby to fetch the Struct using the Data_Get_Struct macro?
Is assigning DATA_PTR(self) to the C pointer going to wreck the ruby
Object being created?

#include <stdio.h>
#include <tsk3/libtsk.h>
#include <ruby.h>

// prototypes
static VALUE rb_mtsk4r;
static VALUE rb_cClass1;
static VALUE initialize(VALUE self);
static VALUE sector_size(VALUE self);
VALUE klass;

// alloc & dealloc
static void deallocate(void * image){
xfree((TSK_IMG_INFO *)image);
}
static VALUE allocate(VALUE klass){
TSK_IMG_INFO *ptr;
return Data_Make_Struct(klass, TSK_IMG_INFO, 0, xfree, ptr);
}

// init a FirstClass (Image) object
VALUE initialize(VALUE self){
char * filename;
filename = “/tmp/test.image”;
TSK_IMG_INFO *image; // declare inner struct
Data_Get_Struct(self, TSK_IMG_INFO, image); // fetch inner struct from
self

// use function from libtsk3 to open disk image
image = tsk_img_open_sing(filename, TSK_IMG_TYPE_DETECT, 0);
fprintf(stdout, “image size: %d\tsector size:%d\n”, (int)image->size,
(int)image->sector_size);

Check_Type(self, T_DATA);

// done
fprintf(stdout, “DATA_PTR(self): %lu\n”, (long)DATA_PTR(self));
DATA_PTR(self) = image; // Not sure I should be doing this…
fprintf(stdout, “DATA_PTR(self): %lu\n”, (long)DATA_PTR(self));

return self;
}

static VALUE sector_size(VALUE self){
TSK_IMG_INFO * image;
Data_Get_Struct(self, TSK_IMG_INFO, image);
return INT2NUM(image->sector_size);
}

void Init_tsk4r() {
rb_mtsk4r = rb_define_module(“TSK”);

// class definition
rb_cClass1 = rb_define_class_under(rb_mtsk4r, “Image”, rb_cObject);

// allocation function
rb_define_alloc_func(rb_cClass1, allocate);

// object methods
rb_define_method(rb_cClass1, “initialize”, initialize, 0);
rb_define_method(rb_cClass1, “sector_size”, sector_size, 0);

}

your alloc method does not create an object, change
TSK_IMG_INFO *ptr;
to
TSK_IMG_INFO *ptr = new TSK_IMG_INFO;

On Dec 5, 2011, at 12:38 AM, Hans M. wrote:

your alloc method does not create an object, change
TSK_IMG_INFO *ptr;
to
TSK_IMG_INFO *ptr = new TSK_IMG_INFO;

Is this a C++ extensions? I would be careful mixing new/delete and
Ruby’s x-allocators. Why would you do that here? Would it not be better
to use xmalloc and Data_Wrap_Struct?

Matthew S. wrote in post #1035055:

// alloc & dealloc
static void deallocate(void * image){
xfree((TSK_IMG_INFO *)image);
}
static VALUE allocate(VALUE klass){
TSK_IMG_INFO *ptr;
return Data_Make_Struct(klass, TSK_IMG_INFO, 0, xfree, ptr);
}

// init a FirstClass (Image) object
VALUE initialize(VALUE self){
char * filename;
filename = “/tmp/test.image”;
TSK_IMG_INFO *image; // declare inner struct
Data_Get_Struct(self, TSK_IMG_INFO, image); // fetch inner struct from
self

// use function from libtsk3 to open disk image
image = tsk_img_open_sing(filename, TSK_IMG_TYPE_DETECT, 0);
fprintf(stdout, “image size: %d\tsector size:%d\n”, (int)image->size,
(int)image->sector_size);

Check_Type(self, T_DATA);

// done
fprintf(stdout, “DATA_PTR(self): %lu\n”, (long)DATA_PTR(self));
DATA_PTR(self) = image; // Not sure I should be doing this…
fprintf(stdout, “DATA_PTR(self): %lu\n”, (long)DATA_PTR(self));

return self;
}

Hi,

This is similar to the issue of pointer and address of pointer.

First, it seems the allocation is done through tsk_img_open_sing(). So
then in your allocate(), you actually want something like this:

static VALUE allocate(VALUE klass){
TSK_IMG_INFO *ptr;
ptr = tsk_img_open_sing( … );
return Data_Wrap_Struct(klass, 0, deallocate, ptr);
}

But are you sure that xfree() is the delete function corresponds to
tsk_img_open_sing()?

With this, the problem is that you cannot pass the filename (except
maybe
through some global variable), so you want to do it in the initialize().
Then instead you can just use DATA_PTR directly:

DATA_PTR(self) = tsk_img_open_sing(filename, TSK_IMG_TYPE_DETECT, 0);

If this seems getting too “unclean”, then you can use an intermediate
handle:

struct myHandle
{
TSK_IMG_INFO *image;
};

static VALUE allocate(VALUE klass){
myHandle *ptr;
return Data_Make_Struct(klass, myHandle, 0, deallocate, ptr);
} // but you have to modify deallocate()

VALUE initialize(VALUE self){

myHandle* ptr;
Data_Get_Struct(self, myHandle, ptr);
ptr->image = tsk_img_open_sing(filename, TSK_IMG_TYPE_DETECT, 0);

}

Hope this helps.

Regards,

Bill

Sylvester K. wrote in post #1035129:

Is this a C++ extensions? I would be careful mixing new/delete and
Ruby’s x-allocators. Why would you do that here? Would it not be better
to use xmalloc and Data_Wrap_Struct?

No. I’m attempting this in pure C. The tsk library is written in cpp,
if that’s relevant. As for Data_Wrap_Struct, I tried that like so:

TSK_IMG_INFO * ptr = malloc(sizeof(TSK_IMG_INFO));
Data_Wrap_Struct(klass, NULL, deallocate, ptr);

I understood Data_Make_Struct to combine these two steps, so I did. I
assumed the product, ptr, is returned when the allocation function ends.
Every example I found returned the Data_Wrap_Struct function, so again I
thought I was on the right track.

I’ve tried malloc and ALLOC, with no change in my tests. Haven’t tried
xalloc yet.

Matthew S. wrote in post #1035396:

static void deallocate(struct myHandle * ptr){
TSK_IMG_INFO *image = ptr->image;
tsk_img_close(image);
}

And don’t forget to deallocate the struct myHandle too. :slight_smile:

Regards,

Bill

First, it seems the allocation is done through tsk_img_open_sing(). So
then in your allocate(), you actually want something like this:

static VALUE allocate(VALUE klass){
TSK_IMG_INFO *ptr;
ptr = tsk_img_open_sing( … );
return Data_Wrap_Struct(klass, 0, deallocate, ptr);
}

But are you sure that xfree() is the delete function corresponds to
tsk_img_open_sing()?

With this, the problem is that you cannot pass the filename (except
maybe
through some global variable), so you want to do it in the initialize().
Then instead you can just use DATA_PTR directly:

DATA_PTR(self) = tsk_img_open_sing(filename, TSK_IMG_TYPE_DETECT, 0);

If this seems getting too “unclean”, then you can use an intermediate
handle:

struct myHandle
{
TSK_IMG_INFO *image;
};

static VALUE allocate(VALUE klass){
myHandle *ptr;
return Data_Make_Struct(klass, myHandle, 0, deallocate, ptr);
} // but you have to modify deallocate()

VALUE initialize(VALUE self){

myHandle* ptr;
Data_Get_Struct(self, myHandle, ptr);
ptr->image = tsk_img_open_sing(filename, TSK_IMG_TYPE_DETECT, 0);

}

Hope this helps.

Regards,

Bill

These were very helpful suggestions. You’re right, the
tsk_img_open_sing() function is the right way to go for allocation, and
the corresponding tsk_img_close() was the proper deallocation function.
Looking into the lib’s source it became clear that the intention was to
protect alloc and dealloc by wrapping them in open() and close()
methods, which makes sense for an I/O module like this. The lesson I
take from this is to follow the source library’s way of doing things,
rather than seek a generic approach.

I was wary of opening the image during the ruby allocate() function, as
that seemed to violate Ruby’s separation of concerns: allocate() is for
reserving memory, while init() is the place for constructing the object
itself.

I used your suggestion to wrap the TSK_IMG_INFO in another struct and my
init() went like so:

struct myHandle* ptr;
Data_Get_Struct(self, struct myHandle, ptr);
ptr->image = tsk_img_open_sing(filename, TSK_IMG_TYPE_DETECT, 0);

with myHandle prototyped as you illustrated. Assigning the
tsk_img_open() function to DATA_PTR(self) worked OK, but it made it
difficult to access components of the TSK_IMG_INFO struct, which made it
impractical.

With the intermediate handle in place, I was able to access the lower
level pointer in other instance methods like so:

TSK_IMG_INFO *image = ptr->image;

Which seems a safer means of retrieval than touching DATA_PTR.

Deallocation had to be re-written as follows, to dereference the
TSK_IMG_INFO pointer:

static void deallocate(struct myHandle * ptr){
TSK_IMG_INFO *image = ptr->image;
tsk_img_close(image);
}

Thanks for the enlightening suggestions.

Matthew S.