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