|
Ruby 1.9.2p290(2011-07-09revision32553)
|
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
1.7.3