1/* -*- C -*- 2 * $Id: cptr.c 44659 2014-01-19 16:28:53Z nagachika $ 3 */ 4 5#include <ruby/ruby.h> 6#include <ruby/io.h> 7#include <ctype.h> 8#include "dl.h" 9 10VALUE rb_cDLCPtr; 11 12static inline freefunc_t 13get_freefunc(VALUE func, volatile VALUE *wrap) 14{ 15 VALUE addrnum; 16 if (NIL_P(func)) { 17 *wrap = 0; 18 return NULL; 19 } 20 if (rb_dlcfunc_kind_p(func)) { 21 *wrap = func; 22 return (freefunc_t)(VALUE)RCFUNC_DATA(func)->ptr; 23 } 24 addrnum = rb_Integer(func); 25 *wrap = (addrnum != func) ? func : 0; 26 return (freefunc_t)(VALUE)NUM2PTR(addrnum); 27} 28 29static ID id_to_ptr; 30 31static void 32dlptr_mark(void *ptr) 33{ 34 struct ptr_data *data = ptr; 35 if (data->wrap[0]) { 36 rb_gc_mark(data->wrap[0]); 37 } 38 if (data->wrap[1]) { 39 rb_gc_mark(data->wrap[1]); 40 } 41} 42 43static void 44dlptr_free(void *ptr) 45{ 46 struct ptr_data *data = ptr; 47 if (data->ptr) { 48 if (data->free) { 49 (*(data->free))(data->ptr); 50 } 51 } 52} 53 54static size_t 55dlptr_memsize(const void *ptr) 56{ 57 const struct ptr_data *data = ptr; 58 return data ? sizeof(*data) + data->size : 0; 59} 60 61static const rb_data_type_t dlptr_data_type = { 62 "dl/ptr", 63 {dlptr_mark, dlptr_free, dlptr_memsize,}, 64}; 65 66VALUE 67rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func) 68{ 69 struct ptr_data *data; 70 VALUE val; 71 72 rb_secure(4); 73 val = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data); 74 data->ptr = ptr; 75 data->free = func; 76 data->size = size; 77 OBJ_TAINT(val); 78 79 return val; 80} 81 82VALUE 83rb_dlptr_new(void *ptr, long size, freefunc_t func) 84{ 85 return rb_dlptr_new2(rb_cDLCPtr, ptr, size, func); 86} 87 88VALUE 89rb_dlptr_malloc(long size, freefunc_t func) 90{ 91 void *ptr; 92 93 rb_secure(4); 94 ptr = ruby_xmalloc((size_t)size); 95 memset(ptr,0,(size_t)size); 96 return rb_dlptr_new(ptr, size, func); 97} 98 99void * 100rb_dlptr2cptr(VALUE val) 101{ 102 struct ptr_data *data; 103 void *ptr; 104 105 if (rb_obj_is_kind_of(val, rb_cDLCPtr)) { 106 TypedData_Get_Struct(val, struct ptr_data, &dlptr_data_type, data); 107 ptr = data->ptr; 108 } 109 else if (val == Qnil) { 110 ptr = NULL; 111 } 112 else{ 113 rb_raise(rb_eTypeError, "DL::PtrData was expected"); 114 } 115 116 return ptr; 117} 118 119static VALUE 120rb_dlptr_s_allocate(VALUE klass) 121{ 122 VALUE obj; 123 struct ptr_data *data; 124 125 rb_secure(4); 126 obj = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data); 127 data->ptr = 0; 128 data->size = 0; 129 data->free = 0; 130 131 return obj; 132} 133 134/* 135 * call-seq: 136 * DL::CPtr.new(address) => dl_cptr 137 * DL::CPtr.new(address, size) => dl_cptr 138 * DL::CPtr.new(address, size, freefunc) => dl_cptr 139 * 140 * Create a new pointer to +address+ with an optional +size+ and +freefunc+. 141 * +freefunc+ will be called when the instance is garbage collected. 142 */ 143static VALUE 144rb_dlptr_initialize(int argc, VALUE argv[], VALUE self) 145{ 146 VALUE ptr, sym, size, wrap = 0, funcwrap = 0; 147 struct ptr_data *data; 148 void *p = NULL; 149 freefunc_t f = NULL; 150 long s = 0; 151 152 if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) { 153 VALUE addrnum = rb_Integer(ptr); 154 if (addrnum != ptr) wrap = ptr; 155 p = NUM2PTR(addrnum); 156 } 157 if (argc >= 2) { 158 s = NUM2LONG(size); 159 } 160 if (argc >= 3) { 161 f = get_freefunc(sym, &funcwrap); 162 } 163 164 if (p) { 165 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 166 if (data->ptr && data->free) { 167 /* Free previous memory. Use of inappropriate initialize may cause SEGV. */ 168 (*(data->free))(data->ptr); 169 } 170 data->wrap[0] = wrap; 171 data->wrap[1] = funcwrap; 172 data->ptr = p; 173 data->size = s; 174 data->free = f; 175 } 176 177 return Qnil; 178} 179 180/* 181 * call-seq: 182 * 183 * DL::CPtr.malloc(size, freefunc = nil) => dl cptr instance 184 * 185 * Allocate +size+ bytes of memory and associate it with an optional 186 * +freefunc+ that will be called when the pointer is garbage collected. 187 * +freefunc+ must be an address pointing to a function or an instance of 188 * DL::CFunc 189 */ 190static VALUE 191rb_dlptr_s_malloc(int argc, VALUE argv[], VALUE klass) 192{ 193 VALUE size, sym, obj, wrap = 0; 194 long s; 195 freefunc_t f; 196 197 switch (rb_scan_args(argc, argv, "11", &size, &sym)) { 198 case 1: 199 s = NUM2LONG(size); 200 f = NULL; 201 break; 202 case 2: 203 s = NUM2LONG(size); 204 f = get_freefunc(sym, &wrap); 205 break; 206 default: 207 rb_bug("rb_dlptr_s_malloc"); 208 } 209 210 obj = rb_dlptr_malloc(s,f); 211 if (wrap) RPTR_DATA(obj)->wrap[1] = wrap; 212 213 return obj; 214} 215 216/* 217 * call-seq: to_i 218 * 219 * Returns the integer memory location of this DL::CPtr. 220 */ 221static VALUE 222rb_dlptr_to_i(VALUE self) 223{ 224 struct ptr_data *data; 225 226 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 227 return PTR2NUM(data->ptr); 228} 229 230/* 231 * call-seq: to_value 232 * 233 * Cast this CPtr to a ruby object. 234 */ 235static VALUE 236rb_dlptr_to_value(VALUE self) 237{ 238 struct ptr_data *data; 239 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 240 return (VALUE)(data->ptr); 241} 242 243/* 244 * call-seq: ptr 245 * 246 * Returns a DL::CPtr that is a dereferenced pointer for this DL::CPtr. 247 * Analogous to the star operator in C. 248 */ 249VALUE 250rb_dlptr_ptr(VALUE self) 251{ 252 struct ptr_data *data; 253 254 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 255 return rb_dlptr_new(*((void**)(data->ptr)),0,0); 256} 257 258/* 259 * call-seq: ref 260 * 261 * Returns a DL::CPtr that is a reference pointer for this DL::CPtr. 262 * Analogous to the ampersand operator in C. 263 */ 264VALUE 265rb_dlptr_ref(VALUE self) 266{ 267 struct ptr_data *data; 268 269 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 270 return rb_dlptr_new(&(data->ptr),0,0); 271} 272 273/* 274 * call-seq: null? 275 * 276 * Returns true if this is a null pointer. 277 */ 278VALUE 279rb_dlptr_null_p(VALUE self) 280{ 281 struct ptr_data *data; 282 283 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 284 return data->ptr ? Qfalse : Qtrue; 285} 286 287/* 288 * call-seq: free=(function) 289 * 290 * Set the free function for this pointer to the DL::CFunc in +function+. 291 */ 292static VALUE 293rb_dlptr_free_set(VALUE self, VALUE val) 294{ 295 struct ptr_data *data; 296 297 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 298 data->free = get_freefunc(val, &data->wrap[1]); 299 300 return Qnil; 301} 302 303/* 304 * call-seq: free 305 * 306 * Get the free function for this pointer. Returns DL::CFunc or nil. 307 */ 308static VALUE 309rb_dlptr_free_get(VALUE self) 310{ 311 struct ptr_data *pdata; 312 313 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, pdata); 314 315 return rb_dlcfunc_new(pdata->free, DLTYPE_VOID, "free<anonymous>", CFUNC_CDECL); 316} 317 318/* 319 * call-seq: 320 * 321 * ptr.to_s => string 322 * ptr.to_s(len) => string 323 * 324 * Returns the pointer contents as a string. When called with no arguments, 325 * this method will return the contents until the first NULL byte. When 326 * called with +len+, a string of +len+ bytes will be returned. 327 */ 328static VALUE 329rb_dlptr_to_s(int argc, VALUE argv[], VALUE self) 330{ 331 struct ptr_data *data; 332 VALUE arg1, val; 333 int len; 334 335 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 336 switch (rb_scan_args(argc, argv, "01", &arg1)) { 337 case 0: 338 val = rb_tainted_str_new2((char*)(data->ptr)); 339 break; 340 case 1: 341 len = NUM2INT(arg1); 342 val = rb_tainted_str_new((char*)(data->ptr), len); 343 break; 344 default: 345 rb_bug("rb_dlptr_to_s"); 346 } 347 348 return val; 349} 350 351/* 352 * call-seq: 353 * 354 * ptr.to_str => string 355 * ptr.to_str(len) => string 356 * 357 * Returns the pointer contents as a string. When called with no arguments, 358 * this method will return the contents with the length of this pointer's 359 * +size+. When called with +len+, a string of +len+ bytes will be returned. 360 */ 361static VALUE 362rb_dlptr_to_str(int argc, VALUE argv[], VALUE self) 363{ 364 struct ptr_data *data; 365 VALUE arg1, val; 366 int len; 367 368 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 369 switch (rb_scan_args(argc, argv, "01", &arg1)) { 370 case 0: 371 val = rb_tainted_str_new((char*)(data->ptr),data->size); 372 break; 373 case 1: 374 len = NUM2INT(arg1); 375 val = rb_tainted_str_new((char*)(data->ptr), len); 376 break; 377 default: 378 rb_bug("rb_dlptr_to_str"); 379 } 380 381 return val; 382} 383 384/* 385 * call-seq: inspect 386 * 387 * Returns a string formatted with an easily readable representation of the 388 * internal state of the DL::CPtr 389 */ 390static VALUE 391rb_dlptr_inspect(VALUE self) 392{ 393 struct ptr_data *data; 394 395 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 396 return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>", 397 rb_obj_class(self), data, data->ptr, data->size, data->free); 398} 399 400/* 401 * call-seq: 402 * ptr == other => true or false 403 * ptr.eql?(other) => true or false 404 * 405 * Returns true if +other+ wraps the same pointer, otherwise returns 406 * false. 407 */ 408VALUE 409rb_dlptr_eql(VALUE self, VALUE other) 410{ 411 void *ptr1, *ptr2; 412 413 if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qfalse; 414 415 ptr1 = rb_dlptr2cptr(self); 416 ptr2 = rb_dlptr2cptr(other); 417 418 return ptr1 == ptr2 ? Qtrue : Qfalse; 419} 420 421/* 422 * call-seq: 423 * ptr <=> other => -1, 0, 1, or nil 424 * 425 * Returns -1 if less than, 0 if equal to, 1 if greater than +other+. Returns 426 * nil if +ptr+ cannot be compared to +other+. 427 */ 428static VALUE 429rb_dlptr_cmp(VALUE self, VALUE other) 430{ 431 void *ptr1, *ptr2; 432 SIGNED_VALUE diff; 433 434 if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qnil; 435 436 ptr1 = rb_dlptr2cptr(self); 437 ptr2 = rb_dlptr2cptr(other); 438 diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2; 439 if (!diff) return INT2FIX(0); 440 return diff > 0 ? INT2NUM(1) : INT2NUM(-1); 441} 442 443/* 444 * call-seq: 445 * ptr + n => new cptr 446 * 447 * Returns a new DL::CPtr that has been advanced +n+ bytes. 448 */ 449static VALUE 450rb_dlptr_plus(VALUE self, VALUE other) 451{ 452 void *ptr; 453 long num, size; 454 455 ptr = rb_dlptr2cptr(self); 456 size = RPTR_DATA(self)->size; 457 num = NUM2LONG(other); 458 return rb_dlptr_new((char *)ptr + num, size - num, 0); 459} 460 461/* 462 * call-seq: 463 * ptr - n => new cptr 464 * 465 * Returns a new DL::CPtr that has been moved back +n+ bytes. 466 */ 467static VALUE 468rb_dlptr_minus(VALUE self, VALUE other) 469{ 470 void *ptr; 471 long num, size; 472 473 ptr = rb_dlptr2cptr(self); 474 size = RPTR_DATA(self)->size; 475 num = NUM2LONG(other); 476 return rb_dlptr_new((char *)ptr - num, size + num, 0); 477} 478 479/* 480 * call-seq: 481 * ptr[index] -> an_integer 482 * ptr[start, length] -> a_string 483 * 484 * Returns integer stored at _index_. If _start_ and _length_ are given, 485 * a string containing the bytes from _start_ of length _length_ will be 486 * returned. 487 */ 488VALUE 489rb_dlptr_aref(int argc, VALUE argv[], VALUE self) 490{ 491 VALUE arg0, arg1; 492 VALUE retval = Qnil; 493 size_t offset, len; 494 struct ptr_data *data; 495 496 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 497 if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference"); 498 switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){ 499 case 1: 500 offset = NUM2ULONG(arg0); 501 retval = INT2NUM(*((char *)data->ptr + offset)); 502 break; 503 case 2: 504 offset = NUM2ULONG(arg0); 505 len = NUM2ULONG(arg1); 506 retval = rb_tainted_str_new((char *)data->ptr + offset, len); 507 break; 508 default: 509 rb_bug("rb_dlptr_aref()"); 510 } 511 return retval; 512} 513 514/* 515 * call-seq: 516 * ptr[index] = int -> int 517 * ptr[start, length] = string or cptr or addr -> string or dl_cptr or addr 518 * 519 * Set the value at +index+ to +int+. Or, set the memory at +start+ until 520 * +length+ with the contents of +string+, the memory from +dl_cptr+, or the 521 * memory pointed at by the memory address +addr+. 522 */ 523VALUE 524rb_dlptr_aset(int argc, VALUE argv[], VALUE self) 525{ 526 VALUE arg0, arg1, arg2; 527 VALUE retval = Qnil; 528 size_t offset, len; 529 void *mem; 530 struct ptr_data *data; 531 532 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data); 533 if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference"); 534 switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){ 535 case 2: 536 offset = NUM2ULONG(arg0); 537 ((char*)data->ptr)[offset] = NUM2UINT(arg1); 538 retval = arg1; 539 break; 540 case 3: 541 offset = NUM2ULONG(arg0); 542 len = NUM2ULONG(arg1); 543 if (RB_TYPE_P(arg2, T_STRING)) { 544 mem = StringValuePtr(arg2); 545 } 546 else if( rb_obj_is_kind_of(arg2, rb_cDLCPtr) ){ 547 mem = rb_dlptr2cptr(arg2); 548 } 549 else{ 550 mem = NUM2PTR(arg2); 551 } 552 memcpy((char *)data->ptr + offset, mem, len); 553 retval = arg2; 554 break; 555 default: 556 rb_bug("rb_dlptr_aset()"); 557 } 558 return retval; 559} 560 561/* 562 * call-seq: size=(size) 563 * 564 * Set the size of this pointer to +size+ 565 */ 566static VALUE 567rb_dlptr_size_set(VALUE self, VALUE size) 568{ 569 RPTR_DATA(self)->size = NUM2LONG(size); 570 return size; 571} 572 573/* 574 * call-seq: size 575 * 576 * Get the size of this pointer. 577 */ 578static VALUE 579rb_dlptr_size_get(VALUE self) 580{ 581 return LONG2NUM(RPTR_DATA(self)->size); 582} 583 584/* 585 * call-seq: 586 * DL::CPtr.to_ptr(val) => cptr 587 * DL::CPtr[val] => cptr 588 * 589 * Get the underlying pointer for ruby object +val+ and return it as a 590 * DL::CPtr object. 591 */ 592static VALUE 593rb_dlptr_s_to_ptr(VALUE self, VALUE val) 594{ 595 VALUE ptr, wrap = val, vptr; 596 597 if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){ 598 rb_io_t *fptr; 599 FILE *fp; 600 GetOpenFile(val, fptr); 601 fp = rb_io_stdio_file(fptr); 602 ptr = rb_dlptr_new(fp, 0, NULL); 603 } 604 else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){ 605 char *str = StringValuePtr(val); 606 ptr = rb_dlptr_new(str, RSTRING_LEN(val), NULL); 607 } 608 else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){ 609 if (rb_obj_is_kind_of(vptr, rb_cDLCPtr)){ 610 ptr = vptr; 611 wrap = 0; 612 } 613 else{ 614 rb_raise(rb_eDLError, "to_ptr should return a CPtr object"); 615 } 616 } 617 else{ 618 VALUE num = rb_Integer(val); 619 if (num == val) wrap = 0; 620 ptr = rb_dlptr_new(NUM2PTR(num), 0, NULL); 621 } 622 OBJ_INFECT(ptr, val); 623 if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap; 624 return ptr; 625} 626 627void 628Init_dlptr(void) 629{ 630 id_to_ptr = rb_intern("to_ptr"); 631 632 /* Document-class: DL::CPtr 633 * 634 * CPtr is a class to handle C pointers 635 * 636 */ 637 rb_cDLCPtr = rb_define_class_under(rb_mDL, "CPtr", rb_cObject); 638 rb_define_alloc_func(rb_cDLCPtr, rb_dlptr_s_allocate); 639 rb_define_singleton_method(rb_cDLCPtr, "malloc", rb_dlptr_s_malloc, -1); 640 rb_define_singleton_method(rb_cDLCPtr, "to_ptr", rb_dlptr_s_to_ptr, 1); 641 rb_define_singleton_method(rb_cDLCPtr, "[]", rb_dlptr_s_to_ptr, 1); 642 rb_define_method(rb_cDLCPtr, "initialize", rb_dlptr_initialize, -1); 643 rb_define_method(rb_cDLCPtr, "free=", rb_dlptr_free_set, 1); 644 rb_define_method(rb_cDLCPtr, "free", rb_dlptr_free_get, 0); 645 rb_define_method(rb_cDLCPtr, "to_i", rb_dlptr_to_i, 0); 646 rb_define_method(rb_cDLCPtr, "to_int", rb_dlptr_to_i, 0); 647 rb_define_method(rb_cDLCPtr, "to_value", rb_dlptr_to_value, 0); 648 rb_define_method(rb_cDLCPtr, "ptr", rb_dlptr_ptr, 0); 649 rb_define_method(rb_cDLCPtr, "+@", rb_dlptr_ptr, 0); 650 rb_define_method(rb_cDLCPtr, "ref", rb_dlptr_ref, 0); 651 rb_define_method(rb_cDLCPtr, "-@", rb_dlptr_ref, 0); 652 rb_define_method(rb_cDLCPtr, "null?", rb_dlptr_null_p, 0); 653 rb_define_method(rb_cDLCPtr, "to_s", rb_dlptr_to_s, -1); 654 rb_define_method(rb_cDLCPtr, "to_str", rb_dlptr_to_str, -1); 655 rb_define_method(rb_cDLCPtr, "inspect", rb_dlptr_inspect, 0); 656 rb_define_method(rb_cDLCPtr, "<=>", rb_dlptr_cmp, 1); 657 rb_define_method(rb_cDLCPtr, "==", rb_dlptr_eql, 1); 658 rb_define_method(rb_cDLCPtr, "eql?", rb_dlptr_eql, 1); 659 rb_define_method(rb_cDLCPtr, "+", rb_dlptr_plus, 1); 660 rb_define_method(rb_cDLCPtr, "-", rb_dlptr_minus, 1); 661 rb_define_method(rb_cDLCPtr, "[]", rb_dlptr_aref, -1); 662 rb_define_method(rb_cDLCPtr, "[]=", rb_dlptr_aset, -1); 663 rb_define_method(rb_cDLCPtr, "size", rb_dlptr_size_get, 0); 664 rb_define_method(rb_cDLCPtr, "size=", rb_dlptr_size_set, 1); 665 666 /* Document-const: NULL 667 * 668 * A NULL pointer 669 */ 670 rb_define_const(rb_mDL, "NULL", rb_dlptr_new(0, 0, 0)); 671} 672