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