ext/dl/cptr.c

Go to the documentation of this file.
00001 /* -*- C -*-
00002  * $Id: cptr.c 28148 2010-06-03 10:33:57Z nobu $
00003  */
00004 
00005 #include <ruby/ruby.h>
00006 #include <ruby/io.h>
00007 #include <ctype.h>
00008 #include "dl.h"
00009 
00010 VALUE rb_cDLCPtr;
00011 
00012 static inline freefunc_t
00013 get_freefunc(VALUE func)
00014 {
00015     if (NIL_P(func)) {
00016         return NULL;
00017     }
00018     if (rb_dlcfunc_kind_p(func)) {
00019         return (freefunc_t)(VALUE)RCFUNC_DATA(func)->ptr;
00020     }
00021     return (freefunc_t)(VALUE)NUM2PTR(rb_Integer(func));
00022 }
00023 
00024 static ID id_to_ptr;
00025 
00026 static void
00027 dlptr_free(void *ptr)
00028 {
00029     struct ptr_data *data = ptr;
00030     if (data->ptr) {
00031         if (data->free) {
00032             (*(data->free))(data->ptr);
00033         }
00034     }
00035 }
00036 
00037 static size_t
00038 dlptr_memsize(const void *ptr)
00039 {
00040     const struct ptr_data *data = ptr;
00041     return data ? sizeof(*data) + data->size : 0;
00042 }
00043 
00044 static const rb_data_type_t dlptr_data_type = {
00045     "dl/ptr",
00046     0, dlptr_free, dlptr_memsize,
00047 };
00048 
00049 void
00050 dlptr_init(VALUE val)
00051 {
00052     struct ptr_data *data;
00053 
00054     TypedData_Get_Struct(val, struct ptr_data, &dlptr_data_type, data);
00055     OBJ_TAINT(val);
00056 }
00057 
00058 VALUE
00059 rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
00060 {
00061     struct ptr_data *data;
00062     VALUE val;
00063 
00064     rb_secure(4);
00065     val = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data);
00066     data->ptr = ptr;
00067     data->free = func;
00068     data->size = size;
00069     dlptr_init(val);
00070 
00071     return val;
00072 }
00073 
00074 VALUE
00075 rb_dlptr_new(void *ptr, long size, freefunc_t func)
00076 {
00077     return rb_dlptr_new2(rb_cDLCPtr, ptr, size, func);
00078 }
00079 
00080 VALUE
00081 rb_dlptr_malloc(long size, freefunc_t func)
00082 {
00083     void *ptr;
00084 
00085     rb_secure(4);
00086     ptr = ruby_xmalloc((size_t)size);
00087     memset(ptr,0,(size_t)size);
00088     return rb_dlptr_new(ptr, size, func);
00089 }
00090 
00091 void *
00092 rb_dlptr2cptr(VALUE val)
00093 {
00094     struct ptr_data *data;
00095     void *ptr;
00096 
00097     if (rb_obj_is_kind_of(val, rb_cDLCPtr)) {
00098         TypedData_Get_Struct(val, struct ptr_data, &dlptr_data_type, data);
00099         ptr = data->ptr;
00100     }
00101     else if (val == Qnil) {
00102         ptr = NULL;
00103     }
00104     else{
00105         rb_raise(rb_eTypeError, "DL::PtrData was expected");
00106     }
00107 
00108     return ptr;
00109 }
00110 
00111 static VALUE
00112 rb_dlptr_s_allocate(VALUE klass)
00113 {
00114     VALUE obj;
00115     struct ptr_data *data;
00116 
00117     rb_secure(4);
00118     obj = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data);
00119     data->ptr = 0;
00120     data->size = 0;
00121     data->free = 0;
00122 
00123     return obj;
00124 }
00125 
00126 /*
00127  * call-seq:
00128  *    DL::CPtr.new(address)                   => dl_cptr
00129  *    DL::CPtr.new(address, size)             => dl_cptr
00130  *    DL::CPtr.new(address, size, freefunc)   => dl_cptr
00131  *
00132  * Create a new pointer to +address+ with an optional +size+ and +freefunc+.
00133  * +freefunc+ will be called when the instance is garbage collected.
00134  */
00135 static VALUE
00136 rb_dlptr_initialize(int argc, VALUE argv[], VALUE self)
00137 {
00138     VALUE ptr, sym, size;
00139     struct ptr_data *data;
00140     void *p = NULL;
00141     freefunc_t f = NULL;
00142     long s = 0;
00143 
00144     switch (rb_scan_args(argc, argv, "12", &ptr, &size, &sym)) {
00145       case 1:
00146         p = (void*)(NUM2PTR(rb_Integer(ptr)));
00147         break;
00148       case 2:
00149         p = (void*)(NUM2PTR(rb_Integer(ptr)));
00150         s = NUM2LONG(size);
00151         break;
00152       case 3:
00153         p = (void*)(NUM2PTR(rb_Integer(ptr)));
00154         s = NUM2LONG(size);
00155         f = get_freefunc(sym);
00156         break;
00157       default:
00158         rb_bug("rb_dlptr_initialize");
00159     }
00160 
00161     if (p) {
00162         TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00163         if (data->ptr && data->free) {
00164             /* Free previous memory. Use of inappropriate initialize may cause SEGV. */
00165             (*(data->free))(data->ptr);
00166         }
00167         data->ptr  = p;
00168         data->size = s;
00169         data->free = f;
00170     }
00171 
00172     return Qnil;
00173 }
00174 
00175 /*
00176  * call-seq:
00177  *
00178  *    DL::CPtr.malloc(size, freefunc = nil)  => dl cptr instance
00179  *
00180  * Allocate +size+ bytes of memory and associate it with an optional
00181  * +freefunc+ that will be called when the pointer is garbage collected.
00182  * +freefunc+ must be an address pointing to a function or an instance of
00183  * DL::CFunc
00184  */
00185 static VALUE
00186 rb_dlptr_s_malloc(int argc, VALUE argv[], VALUE klass)
00187 {
00188     VALUE size, sym, obj;
00189     long s;
00190     freefunc_t f;
00191 
00192     switch (rb_scan_args(argc, argv, "11", &size, &sym)) {
00193       case 1:
00194         s = NUM2LONG(size);
00195         f = NULL;
00196         break;
00197       case 2:
00198         s = NUM2LONG(size);
00199         f = get_freefunc(sym);
00200         break;
00201       default:
00202         rb_bug("rb_dlptr_s_malloc");
00203     }
00204 
00205     obj = rb_dlptr_malloc(s,f);
00206 
00207     return obj;
00208 }
00209 
00210 /*
00211  * call-seq: to_i
00212  *
00213  * Returns the integer memory location of this DL::CPtr.
00214  */
00215 static VALUE
00216 rb_dlptr_to_i(VALUE self)
00217 {
00218     struct ptr_data *data;
00219 
00220     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00221     return PTR2NUM(data->ptr);
00222 }
00223 
00224 /*
00225  * call-seq: to_value
00226  *
00227  * Cast this CPtr to a ruby object.
00228  */
00229 static VALUE
00230 rb_dlptr_to_value(VALUE self)
00231 {
00232     struct ptr_data *data;
00233     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00234     return (VALUE)(data->ptr);
00235 }
00236 
00237 /*
00238  * call-seq: ptr
00239  *
00240  * Returns a DL::CPtr that is a dereferenced pointer for this DL::CPtr.
00241  * Analogous to the star operator in C.
00242  */
00243 VALUE
00244 rb_dlptr_ptr(VALUE self)
00245 {
00246     struct ptr_data *data;
00247 
00248     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00249     return rb_dlptr_new(*((void**)(data->ptr)),0,0);
00250 }
00251 
00252 /*
00253  * call-seq: ref
00254  *
00255  * Returns a DL::CPtr that is a reference pointer for this DL::CPtr.
00256  * Analogous to the ampersand operator in C.
00257  */
00258 VALUE
00259 rb_dlptr_ref(VALUE self)
00260 {
00261     struct ptr_data *data;
00262 
00263     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00264     return rb_dlptr_new(&(data->ptr),0,0);
00265 }
00266 
00267 /*
00268  * call-seq: null?
00269  *
00270  * Returns true if this is a null pointer.
00271  */
00272 VALUE
00273 rb_dlptr_null_p(VALUE self)
00274 {
00275     struct ptr_data *data;
00276 
00277     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00278     return data->ptr ? Qfalse : Qtrue;
00279 }
00280 
00281 /*
00282  * call-seq: free=(function)
00283  *
00284  * Set the free function for this pointer to the DL::CFunc in +function+.
00285  */
00286 static VALUE
00287 rb_dlptr_free_set(VALUE self, VALUE val)
00288 {
00289     struct ptr_data *data;
00290 
00291     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00292     data->free = get_freefunc(val);
00293 
00294     return Qnil;
00295 }
00296 
00297 /*
00298  * call-seq: free
00299  *
00300  * Get the free function for this pointer.  Returns  DL::CFunc or nil.
00301  */
00302 static VALUE
00303 rb_dlptr_free_get(VALUE self)
00304 {
00305     struct ptr_data *pdata;
00306 
00307     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, pdata);
00308 
00309     return rb_dlcfunc_new(pdata->free, DLTYPE_VOID, "free<anonymous>", CFUNC_CDECL);
00310 }
00311 
00312 /*
00313  * call-seq:
00314  *
00315  *    ptr.to_s        => string
00316  *    ptr.to_s(len)   => string
00317  *
00318  * Returns the pointer contents as a string.  When called with no arguments,
00319  * this method will return the contents until the first NULL byte.  When
00320  * called with +len+, a string of +len+ bytes will be returned.
00321  */
00322 static VALUE
00323 rb_dlptr_to_s(int argc, VALUE argv[], VALUE self)
00324 {
00325     struct ptr_data *data;
00326     VALUE arg1, val;
00327     int len;
00328 
00329     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00330     switch (rb_scan_args(argc, argv, "01", &arg1)) {
00331       case 0:
00332         val = rb_tainted_str_new2((char*)(data->ptr));
00333         break;
00334       case 1:
00335         len = NUM2INT(arg1);
00336         val = rb_tainted_str_new((char*)(data->ptr), len);
00337         break;
00338       default:
00339         rb_bug("rb_dlptr_to_s");
00340     }
00341 
00342     return val;
00343 }
00344 
00345 /*
00346  * call-seq:
00347  *
00348  *    ptr.to_str        => string
00349  *    ptr.to_str(len)   => string
00350  *
00351  * Returns the pointer contents as a string.  When called with no arguments,
00352  * this method will return the contents with the length of this pointer's
00353  * +size+. When called with +len+, a string of +len+ bytes will be returned.
00354  */
00355 static VALUE
00356 rb_dlptr_to_str(int argc, VALUE argv[], VALUE self)
00357 {
00358     struct ptr_data *data;
00359     VALUE arg1, val;
00360     int len;
00361 
00362     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00363     switch (rb_scan_args(argc, argv, "01", &arg1)) {
00364       case 0:
00365         val = rb_tainted_str_new((char*)(data->ptr),data->size);
00366         break;
00367       case 1:
00368         len = NUM2INT(arg1);
00369         val = rb_tainted_str_new((char*)(data->ptr), len);
00370         break;
00371       default:
00372         rb_bug("rb_dlptr_to_str");
00373     }
00374 
00375     return val;
00376 }
00377 
00378 /*
00379  * call-seq: inspect
00380  *
00381  * Returns a string formatted with an easily readable representation of the
00382  * internal state of the DL::CPtr
00383  */
00384 static VALUE
00385 rb_dlptr_inspect(VALUE self)
00386 {
00387     struct ptr_data *data;
00388     char str[1024];
00389 
00390     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00391     snprintf(str, 1023, "#<%s:%p ptr=%p size=%ld free=%p>",
00392              rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free);
00393     return rb_str_new2(str);
00394 }
00395 
00396 /*
00397  *  call-seq:
00398  *    ptr == other    => true or false
00399  *    ptr.eql?(other) => true or false
00400  *
00401  * Returns true if +other+ wraps the same pointer, otherwise returns
00402  * false.
00403  */
00404 VALUE
00405 rb_dlptr_eql(VALUE self, VALUE other)
00406 {
00407     void *ptr1, *ptr2;
00408 
00409     if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qfalse;
00410 
00411     ptr1 = rb_dlptr2cptr(self);
00412     ptr2 = rb_dlptr2cptr(other);
00413 
00414     return ptr1 == ptr2 ? Qtrue : Qfalse;
00415 }
00416 
00417 /*
00418  *  call-seq:
00419  *    ptr <=> other   => -1, 0, 1, or nil
00420  *
00421  * Returns -1 if less than, 0 if equal to, 1 if greater than +other+.  Returns
00422  * nil if +ptr+ cannot be compared to +other+.
00423  */
00424 static VALUE
00425 rb_dlptr_cmp(VALUE self, VALUE other)
00426 {
00427     void *ptr1, *ptr2;
00428     SIGNED_VALUE diff;
00429 
00430     if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qnil;
00431 
00432     ptr1 = rb_dlptr2cptr(self);
00433     ptr2 = rb_dlptr2cptr(other);
00434     diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2;
00435     if (!diff) return INT2FIX(0);
00436     return diff > 0 ? INT2NUM(1) : INT2NUM(-1);
00437 }
00438 
00439 /*
00440  * call-seq:
00441  *    ptr + n   => new cptr
00442  *
00443  * Returns a new DL::CPtr that has been advanced +n+ bytes.
00444  */
00445 static VALUE
00446 rb_dlptr_plus(VALUE self, VALUE other)
00447 {
00448     void *ptr;
00449     long num, size;
00450 
00451     ptr = rb_dlptr2cptr(self);
00452     size = RPTR_DATA(self)->size;
00453     num = NUM2LONG(other);
00454     return rb_dlptr_new((char *)ptr + num, size - num, 0);
00455 }
00456 
00457 /*
00458  * call-seq:
00459  *    ptr - n   => new cptr
00460  *
00461  * Returns a new DL::CPtr that has been moved back +n+ bytes.
00462  */
00463 static VALUE
00464 rb_dlptr_minus(VALUE self, VALUE other)
00465 {
00466     void *ptr;
00467     long num, size;
00468 
00469     ptr = rb_dlptr2cptr(self);
00470     size = RPTR_DATA(self)->size;
00471     num = NUM2LONG(other);
00472     return rb_dlptr_new((char *)ptr - num, size + num, 0);
00473 }
00474 
00475 /*
00476  *  call-seq:
00477  *     ptr[index]                -> an_integer
00478  *     ptr[start, length]        -> a_string
00479  *
00480  * Returns integer stored at _index_.  If _start_ and _length_ are given,
00481  * a string containing the bytes from _start_ of length _length_ will be
00482  * returned.
00483  */
00484 VALUE
00485 rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
00486 {
00487     VALUE arg0, arg1;
00488     VALUE retval = Qnil;
00489     size_t offset, len;
00490 
00491     switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
00492       case 1:
00493         offset = NUM2ULONG(arg0);
00494         retval = INT2NUM(*((char*)RPTR_DATA(self)->ptr + offset));
00495         break;
00496       case 2:
00497         offset = NUM2ULONG(arg0);
00498         len    = NUM2ULONG(arg1);
00499         retval = rb_tainted_str_new((char *)RPTR_DATA(self)->ptr + offset, len);
00500         break;
00501       default:
00502         rb_bug("rb_dlptr_aref()");
00503     }
00504     return retval;
00505 }
00506 
00507 /*
00508  *  call-seq:
00509  *     ptr[index]         = int                    ->  int
00510  *     ptr[start, length] = string or cptr or addr ->  string or dl_cptr or addr
00511  *
00512  * Set the value at +index+ to +int+.  Or, set the memory at +start+ until
00513  * +length+ with the contents of +string+, the memory from +dl_cptr+, or the
00514  * memory pointed at by the memory address +addr+.
00515  */
00516 VALUE
00517 rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
00518 {
00519     VALUE arg0, arg1, arg2;
00520     VALUE retval = Qnil;
00521     size_t offset, len;
00522     void *mem;
00523 
00524     switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
00525       case 2:
00526         offset = NUM2ULONG(arg0);
00527         ((char*)RPTR_DATA(self)->ptr)[offset] = NUM2UINT(arg1);
00528         retval = arg1;
00529         break;
00530       case 3:
00531         offset = NUM2ULONG(arg0);
00532         len    = NUM2ULONG(arg1);
00533         if( TYPE(arg2) == T_STRING ){
00534             mem = StringValuePtr(arg2);
00535         }
00536         else if( rb_obj_is_kind_of(arg2, rb_cDLCPtr) ){
00537             mem = rb_dlptr2cptr(arg2);
00538         }
00539         else{
00540             mem    = NUM2PTR(arg2);
00541         }
00542         memcpy((char *)RPTR_DATA(self)->ptr + offset, mem, len);
00543         retval = arg2;
00544         break;
00545       default:
00546         rb_bug("rb_dlptr_aset()");
00547     }
00548     return retval;
00549 }
00550 
00551 /*
00552  * call-seq: size=(size)
00553  *
00554  * Set the size of this pointer to +size+
00555  */
00556 static VALUE
00557 rb_dlptr_size_set(VALUE self, VALUE size)
00558 {
00559     RPTR_DATA(self)->size = NUM2LONG(size);
00560     return size;
00561 }
00562 
00563 /*
00564  * call-seq: size
00565  *
00566  * Get the size of this pointer.
00567  */
00568 static VALUE
00569 rb_dlptr_size_get(VALUE self)
00570 {
00571     return LONG2NUM(RPTR_DATA(self)->size);
00572 }
00573 
00574 /*
00575  * call-seq:
00576  *    DL::CPtr.to_ptr(val)  => cptr
00577  *    DL::CPtr[val]         => cptr
00578  *
00579  * Get the underlying pointer for ruby object +val+ and return it as a
00580  * DL::CPtr object.
00581  */
00582 static VALUE
00583 rb_dlptr_s_to_ptr(VALUE self, VALUE val)
00584 {
00585     VALUE ptr;
00586 
00587     if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){
00588         rb_io_t *fptr;
00589         FILE *fp;
00590         GetOpenFile(val, fptr);
00591         fp = rb_io_stdio_file(fptr);
00592         ptr = rb_dlptr_new(fp, 0, NULL);
00593     }
00594     else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){
00595         char *str = StringValuePtr(val);
00596         ptr = rb_dlptr_new(str, RSTRING_LEN(val), NULL);
00597     }
00598     else if (rb_respond_to(val, id_to_ptr)){
00599         VALUE vptr = rb_funcall(val, id_to_ptr, 0);
00600         if (rb_obj_is_kind_of(vptr, rb_cDLCPtr)){
00601             ptr = vptr;
00602         }
00603         else{
00604             rb_raise(rb_eDLError, "to_ptr should return a CPtr object");
00605         }
00606     }
00607     else{
00608         ptr = rb_dlptr_new(NUM2PTR(rb_Integer(val)), 0, NULL);
00609     }
00610     OBJ_INFECT(ptr, val);
00611     rb_iv_set(ptr, "wrapping", val);
00612     return ptr;
00613 }
00614 
00615 void
00616 Init_dlptr(void)
00617 {
00618     id_to_ptr = rb_intern("to_ptr");
00619 
00620     rb_cDLCPtr = rb_define_class_under(rb_mDL, "CPtr", rb_cObject);
00621     rb_define_alloc_func(rb_cDLCPtr, rb_dlptr_s_allocate);
00622     rb_define_singleton_method(rb_cDLCPtr, "malloc", rb_dlptr_s_malloc, -1);
00623     rb_define_singleton_method(rb_cDLCPtr, "to_ptr", rb_dlptr_s_to_ptr, 1);
00624     rb_define_singleton_method(rb_cDLCPtr, "[]", rb_dlptr_s_to_ptr, 1);
00625     rb_define_method(rb_cDLCPtr, "initialize", rb_dlptr_initialize, -1);
00626     rb_define_method(rb_cDLCPtr, "free=", rb_dlptr_free_set, 1);
00627     rb_define_method(rb_cDLCPtr, "free",  rb_dlptr_free_get, 0);
00628     rb_define_method(rb_cDLCPtr, "to_i",  rb_dlptr_to_i, 0);
00629     rb_define_method(rb_cDLCPtr, "to_int",  rb_dlptr_to_i, 0);
00630     rb_define_method(rb_cDLCPtr, "to_value",  rb_dlptr_to_value, 0);
00631     rb_define_method(rb_cDLCPtr, "ptr",   rb_dlptr_ptr, 0);
00632     rb_define_method(rb_cDLCPtr, "+@", rb_dlptr_ptr, 0);
00633     rb_define_method(rb_cDLCPtr, "ref",   rb_dlptr_ref, 0);
00634     rb_define_method(rb_cDLCPtr, "-@", rb_dlptr_ref, 0);
00635     rb_define_method(rb_cDLCPtr, "null?", rb_dlptr_null_p, 0);
00636     rb_define_method(rb_cDLCPtr, "to_s", rb_dlptr_to_s, -1);
00637     rb_define_method(rb_cDLCPtr, "to_str", rb_dlptr_to_str, -1);
00638     rb_define_method(rb_cDLCPtr, "inspect", rb_dlptr_inspect, 0);
00639     rb_define_method(rb_cDLCPtr, "<=>", rb_dlptr_cmp, 1);
00640     rb_define_method(rb_cDLCPtr, "==", rb_dlptr_eql, 1);
00641     rb_define_method(rb_cDLCPtr, "eql?", rb_dlptr_eql, 1);
00642     rb_define_method(rb_cDLCPtr, "+", rb_dlptr_plus, 1);
00643     rb_define_method(rb_cDLCPtr, "-", rb_dlptr_minus, 1);
00644     rb_define_method(rb_cDLCPtr, "[]", rb_dlptr_aref, -1);
00645     rb_define_method(rb_cDLCPtr, "[]=", rb_dlptr_aset, -1);
00646     rb_define_method(rb_cDLCPtr, "size", rb_dlptr_size_get, 0);
00647     rb_define_method(rb_cDLCPtr, "size=", rb_dlptr_size_set, 1);
00648 
00649     rb_define_const(rb_mDL, "NULL", rb_dlptr_new(0, 0, 0));
00650 }
00651 

Generated on Wed Aug 10 09:16:59 2011 for Ruby by  doxygen 1.4.7