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