00001
00002
00003
00004
00005 #include <ruby/ruby.h>
00006 #include <ruby/io.h>
00007 #include <ctype.h>
00008 #include <fiddle.h>
00009
00010 #ifdef PRIsVALUE
00011 # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
00012 # define RB_OBJ_STRING(obj) (obj)
00013 #else
00014 # define PRIsVALUE "s"
00015 # define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
00016 # define RB_OBJ_STRING(obj) StringValueCStr(obj)
00017 #endif
00018
00019 VALUE rb_cPointer;
00020
00021 typedef void (*freefunc_t)(void*);
00022
00023 struct ptr_data {
00024 void *ptr;
00025 long size;
00026 freefunc_t free;
00027 VALUE wrap[2];
00028 };
00029
00030 #define RPTR_DATA(obj) ((struct ptr_data *)(DATA_PTR(obj)))
00031
00032 static inline freefunc_t
00033 get_freefunc(VALUE func, volatile VALUE *wrap)
00034 {
00035 VALUE addrnum;
00036 if (NIL_P(func)) {
00037 *wrap = 0;
00038 return NULL;
00039 }
00040 addrnum = rb_Integer(func);
00041 *wrap = (addrnum != func) ? func : 0;
00042 return (freefunc_t)(VALUE)NUM2PTR(addrnum);
00043 }
00044
00045 static ID id_to_ptr;
00046
00047 static void
00048 fiddle_ptr_mark(void *ptr)
00049 {
00050 struct ptr_data *data = ptr;
00051 if (data->wrap[0]) {
00052 rb_gc_mark(data->wrap[0]);
00053 }
00054 if (data->wrap[1]) {
00055 rb_gc_mark(data->wrap[1]);
00056 }
00057 }
00058
00059 static void
00060 fiddle_ptr_free(void *ptr)
00061 {
00062 struct ptr_data *data = ptr;
00063 if (data->ptr) {
00064 if (data->free) {
00065 (*(data->free))(data->ptr);
00066 }
00067 }
00068 xfree(ptr);
00069 }
00070
00071 static size_t
00072 fiddle_ptr_memsize(const void *ptr)
00073 {
00074 const struct ptr_data *data = ptr;
00075 return data ? sizeof(*data) + data->size : 0;
00076 }
00077
00078 static const rb_data_type_t fiddle_ptr_data_type = {
00079 "fiddle/pointer",
00080 {fiddle_ptr_mark, fiddle_ptr_free, fiddle_ptr_memsize,},
00081 };
00082
00083 static VALUE
00084 rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
00085 {
00086 struct ptr_data *data;
00087 VALUE val;
00088
00089 rb_secure(4);
00090 val = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data);
00091 data->ptr = ptr;
00092 data->free = func;
00093 data->size = size;
00094 OBJ_TAINT(val);
00095
00096 return val;
00097 }
00098
00099 static VALUE
00100 rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func)
00101 {
00102 return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func);
00103 }
00104
00105 static VALUE
00106 rb_fiddle_ptr_malloc(long size, freefunc_t func)
00107 {
00108 void *ptr;
00109
00110 rb_secure(4);
00111 ptr = ruby_xmalloc((size_t)size);
00112 memset(ptr,0,(size_t)size);
00113 return rb_fiddle_ptr_new(ptr, size, func);
00114 }
00115
00116 static void *
00117 rb_fiddle_ptr2cptr(VALUE val)
00118 {
00119 struct ptr_data *data;
00120 void *ptr;
00121
00122 if (rb_obj_is_kind_of(val, rb_cPointer)) {
00123 TypedData_Get_Struct(val, struct ptr_data, &fiddle_ptr_data_type, data);
00124 ptr = data->ptr;
00125 }
00126 else if (val == Qnil) {
00127 ptr = NULL;
00128 }
00129 else{
00130 rb_raise(rb_eTypeError, "Fiddle::Pointer was expected");
00131 }
00132
00133 return ptr;
00134 }
00135
00136 static VALUE
00137 rb_fiddle_ptr_s_allocate(VALUE klass)
00138 {
00139 VALUE obj;
00140 struct ptr_data *data;
00141
00142 rb_secure(4);
00143 obj = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data);
00144 data->ptr = 0;
00145 data->size = 0;
00146 data->free = 0;
00147
00148 return obj;
00149 }
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 static VALUE
00162 rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self)
00163 {
00164 VALUE ptr, sym, size, wrap = 0, funcwrap = 0;
00165 struct ptr_data *data;
00166 void *p = NULL;
00167 freefunc_t f = NULL;
00168 long s = 0;
00169
00170 if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) {
00171 VALUE addrnum = rb_Integer(ptr);
00172 if (addrnum != ptr) wrap = ptr;
00173 p = NUM2PTR(addrnum);
00174 }
00175 if (argc >= 2) {
00176 s = NUM2LONG(size);
00177 }
00178 if (argc >= 3) {
00179 f = get_freefunc(sym, &funcwrap);
00180 }
00181
00182 if (p) {
00183 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00184 if (data->ptr && data->free) {
00185
00186 (*(data->free))(data->ptr);
00187 }
00188 data->wrap[0] = wrap;
00189 data->wrap[1] = funcwrap;
00190 data->ptr = p;
00191 data->size = s;
00192 data->free = f;
00193 }
00194
00195 return Qnil;
00196 }
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209 static VALUE
00210 rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
00211 {
00212 VALUE size, sym, obj, wrap = 0;
00213 long s;
00214 freefunc_t f;
00215
00216 switch (rb_scan_args(argc, argv, "11", &size, &sym)) {
00217 case 1:
00218 s = NUM2LONG(size);
00219 f = NULL;
00220 break;
00221 case 2:
00222 s = NUM2LONG(size);
00223 f = get_freefunc(sym, &wrap);
00224 break;
00225 default:
00226 rb_bug("rb_fiddle_ptr_s_malloc");
00227 }
00228
00229 obj = rb_fiddle_ptr_malloc(s,f);
00230 if (wrap) RPTR_DATA(obj)->wrap[1] = wrap;
00231
00232 return obj;
00233 }
00234
00235
00236
00237
00238
00239
00240 static VALUE
00241 rb_fiddle_ptr_to_i(VALUE self)
00242 {
00243 struct ptr_data *data;
00244
00245 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00246 return PTR2NUM(data->ptr);
00247 }
00248
00249
00250
00251
00252
00253
00254 static VALUE
00255 rb_fiddle_ptr_to_value(VALUE self)
00256 {
00257 struct ptr_data *data;
00258 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00259 return (VALUE)(data->ptr);
00260 }
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270 static VALUE
00271 rb_fiddle_ptr_ptr(VALUE self)
00272 {
00273 struct ptr_data *data;
00274
00275 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00276 return rb_fiddle_ptr_new(*((void**)(data->ptr)),0,0);
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 static VALUE
00288 rb_fiddle_ptr_ref(VALUE self)
00289 {
00290 struct ptr_data *data;
00291
00292 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00293 return rb_fiddle_ptr_new(&(data->ptr),0,0);
00294 }
00295
00296
00297
00298
00299
00300
00301 static VALUE
00302 rb_fiddle_ptr_null_p(VALUE self)
00303 {
00304 struct ptr_data *data;
00305
00306 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00307 return data->ptr ? Qfalse : Qtrue;
00308 }
00309
00310
00311
00312
00313
00314
00315
00316 static VALUE
00317 rb_fiddle_ptr_free_set(VALUE self, VALUE val)
00318 {
00319 struct ptr_data *data;
00320
00321 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00322 data->free = get_freefunc(val, &data->wrap[1]);
00323
00324 return Qnil;
00325 }
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336 static VALUE
00337 rb_fiddle_ptr_free_get(VALUE self)
00338 {
00339 struct ptr_data *pdata;
00340 VALUE address;
00341 VALUE arg_types;
00342 VALUE ret_type;
00343
00344 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata);
00345
00346 if (!pdata->free)
00347 return Qnil;
00348
00349 address = PTR2NUM(pdata->free);
00350 ret_type = INT2NUM(TYPE_VOID);
00351 arg_types = rb_ary_new();
00352 rb_ary_push(arg_types, INT2NUM(TYPE_VOIDP));
00353
00354 return rb_fiddle_new_function(address, arg_types, ret_type);
00355 }
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372 static VALUE
00373 rb_fiddle_ptr_to_s(int argc, VALUE argv[], VALUE self)
00374 {
00375 struct ptr_data *data;
00376 VALUE arg1, val;
00377 int len;
00378
00379 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00380 switch (rb_scan_args(argc, argv, "01", &arg1)) {
00381 case 0:
00382 val = rb_tainted_str_new2((char*)(data->ptr));
00383 break;
00384 case 1:
00385 len = NUM2INT(arg1);
00386 val = rb_tainted_str_new((char*)(data->ptr), len);
00387 break;
00388 default:
00389 rb_bug("rb_fiddle_ptr_to_s");
00390 }
00391
00392 return val;
00393 }
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410 static VALUE
00411 rb_fiddle_ptr_to_str(int argc, VALUE argv[], VALUE self)
00412 {
00413 struct ptr_data *data;
00414 VALUE arg1, val;
00415 int len;
00416
00417 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00418 switch (rb_scan_args(argc, argv, "01", &arg1)) {
00419 case 0:
00420 val = rb_tainted_str_new((char*)(data->ptr),data->size);
00421 break;
00422 case 1:
00423 len = NUM2INT(arg1);
00424 val = rb_tainted_str_new((char*)(data->ptr), len);
00425 break;
00426 default:
00427 rb_bug("rb_fiddle_ptr_to_str");
00428 }
00429
00430 return val;
00431 }
00432
00433
00434
00435
00436
00437
00438
00439 static VALUE
00440 rb_fiddle_ptr_inspect(VALUE self)
00441 {
00442 struct ptr_data *data;
00443
00444 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00445 return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>",
00446 RB_OBJ_CLASSNAME(self), data, data->ptr, data->size, data->free);
00447 }
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457 static VALUE
00458 rb_fiddle_ptr_eql(VALUE self, VALUE other)
00459 {
00460 void *ptr1, *ptr2;
00461
00462 if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qfalse;
00463
00464 ptr1 = rb_fiddle_ptr2cptr(self);
00465 ptr2 = rb_fiddle_ptr2cptr(other);
00466
00467 return ptr1 == ptr2 ? Qtrue : Qfalse;
00468 }
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478 static VALUE
00479 rb_fiddle_ptr_cmp(VALUE self, VALUE other)
00480 {
00481 void *ptr1, *ptr2;
00482 SIGNED_VALUE diff;
00483
00484 if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qnil;
00485
00486 ptr1 = rb_fiddle_ptr2cptr(self);
00487 ptr2 = rb_fiddle_ptr2cptr(other);
00488 diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2;
00489 if (!diff) return INT2FIX(0);
00490 return diff > 0 ? INT2NUM(1) : INT2NUM(-1);
00491 }
00492
00493
00494
00495
00496
00497
00498
00499 static VALUE
00500 rb_fiddle_ptr_plus(VALUE self, VALUE other)
00501 {
00502 void *ptr;
00503 long num, size;
00504
00505 ptr = rb_fiddle_ptr2cptr(self);
00506 size = RPTR_DATA(self)->size;
00507 num = NUM2LONG(other);
00508 return rb_fiddle_ptr_new((char *)ptr + num, size - num, 0);
00509 }
00510
00511
00512
00513
00514
00515
00516
00517 static VALUE
00518 rb_fiddle_ptr_minus(VALUE self, VALUE other)
00519 {
00520 void *ptr;
00521 long num, size;
00522
00523 ptr = rb_fiddle_ptr2cptr(self);
00524 size = RPTR_DATA(self)->size;
00525 num = NUM2LONG(other);
00526 return rb_fiddle_ptr_new((char *)ptr - num, size + num, 0);
00527 }
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539 static VALUE
00540 rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self)
00541 {
00542 VALUE arg0, arg1;
00543 VALUE retval = Qnil;
00544 size_t offset, len;
00545 struct ptr_data *data;
00546
00547 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00548 if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
00549 switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
00550 case 1:
00551 offset = NUM2ULONG(arg0);
00552 retval = INT2NUM(*((char *)data->ptr + offset));
00553 break;
00554 case 2:
00555 offset = NUM2ULONG(arg0);
00556 len = NUM2ULONG(arg1);
00557 retval = rb_tainted_str_new((char *)data->ptr + offset, len);
00558 break;
00559 default:
00560 rb_bug("rb_fiddle_ptr_aref()");
00561 }
00562 return retval;
00563 }
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576 static VALUE
00577 rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self)
00578 {
00579 VALUE arg0, arg1, arg2;
00580 VALUE retval = Qnil;
00581 size_t offset, len;
00582 void *mem;
00583 struct ptr_data *data;
00584
00585 TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
00586 if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
00587 switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
00588 case 2:
00589 offset = NUM2ULONG(arg0);
00590 ((char*)data->ptr)[offset] = NUM2UINT(arg1);
00591 retval = arg1;
00592 break;
00593 case 3:
00594 offset = NUM2ULONG(arg0);
00595 len = NUM2ULONG(arg1);
00596 if (RB_TYPE_P(arg2, T_STRING)) {
00597 mem = StringValuePtr(arg2);
00598 }
00599 else if( rb_obj_is_kind_of(arg2, rb_cPointer) ){
00600 mem = rb_fiddle_ptr2cptr(arg2);
00601 }
00602 else{
00603 mem = NUM2PTR(arg2);
00604 }
00605 memcpy((char *)data->ptr + offset, mem, len);
00606 retval = arg2;
00607 break;
00608 default:
00609 rb_bug("rb_fiddle_ptr_aset()");
00610 }
00611 return retval;
00612 }
00613
00614
00615
00616
00617
00618
00619 static VALUE
00620 rb_fiddle_ptr_size_set(VALUE self, VALUE size)
00621 {
00622 RPTR_DATA(self)->size = NUM2LONG(size);
00623 return size;
00624 }
00625
00626
00627
00628
00629
00630
00631 static VALUE
00632 rb_fiddle_ptr_size_get(VALUE self)
00633 {
00634 return LONG2NUM(RPTR_DATA(self)->size);
00635 }
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645 static VALUE
00646 rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val)
00647 {
00648 VALUE ptr, wrap = val, vptr;
00649
00650 if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){
00651 rb_io_t *fptr;
00652 FILE *fp;
00653 GetOpenFile(val, fptr);
00654 fp = rb_io_stdio_file(fptr);
00655 ptr = rb_fiddle_ptr_new(fp, 0, NULL);
00656 }
00657 else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){
00658 char *str = StringValuePtr(val);
00659 ptr = rb_fiddle_ptr_new(str, RSTRING_LEN(val), NULL);
00660 }
00661 else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){
00662 if (rb_obj_is_kind_of(vptr, rb_cPointer)){
00663 ptr = vptr;
00664 wrap = 0;
00665 }
00666 else{
00667 rb_raise(rb_eFiddleError, "to_ptr should return a Fiddle::Pointer object");
00668 }
00669 }
00670 else{
00671 VALUE num = rb_Integer(val);
00672 if (num == val) wrap = 0;
00673 ptr = rb_fiddle_ptr_new(NUM2PTR(num), 0, NULL);
00674 }
00675 OBJ_INFECT(ptr, val);
00676 if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap;
00677 return ptr;
00678 }
00679
00680 void
00681 Init_fiddle_pointer(void)
00682 {
00683 id_to_ptr = rb_intern("to_ptr");
00684
00685
00686
00687
00688
00689
00690 rb_cPointer = rb_define_class_under(mFiddle, "Pointer", rb_cObject);
00691 rb_define_alloc_func(rb_cPointer, rb_fiddle_ptr_s_allocate);
00692 rb_define_singleton_method(rb_cPointer, "malloc", rb_fiddle_ptr_s_malloc, -1);
00693 rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1);
00694 rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1);
00695 rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1);
00696 rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1);
00697 rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0);
00698 rb_define_method(rb_cPointer, "to_i", rb_fiddle_ptr_to_i, 0);
00699 rb_define_method(rb_cPointer, "to_int", rb_fiddle_ptr_to_i, 0);
00700 rb_define_method(rb_cPointer, "to_value", rb_fiddle_ptr_to_value, 0);
00701 rb_define_method(rb_cPointer, "ptr", rb_fiddle_ptr_ptr, 0);
00702 rb_define_method(rb_cPointer, "+@", rb_fiddle_ptr_ptr, 0);
00703 rb_define_method(rb_cPointer, "ref", rb_fiddle_ptr_ref, 0);
00704 rb_define_method(rb_cPointer, "-@", rb_fiddle_ptr_ref, 0);
00705 rb_define_method(rb_cPointer, "null?", rb_fiddle_ptr_null_p, 0);
00706 rb_define_method(rb_cPointer, "to_s", rb_fiddle_ptr_to_s, -1);
00707 rb_define_method(rb_cPointer, "to_str", rb_fiddle_ptr_to_str, -1);
00708 rb_define_method(rb_cPointer, "inspect", rb_fiddle_ptr_inspect, 0);
00709 rb_define_method(rb_cPointer, "<=>", rb_fiddle_ptr_cmp, 1);
00710 rb_define_method(rb_cPointer, "==", rb_fiddle_ptr_eql, 1);
00711 rb_define_method(rb_cPointer, "eql?", rb_fiddle_ptr_eql, 1);
00712 rb_define_method(rb_cPointer, "+", rb_fiddle_ptr_plus, 1);
00713 rb_define_method(rb_cPointer, "-", rb_fiddle_ptr_minus, 1);
00714 rb_define_method(rb_cPointer, "[]", rb_fiddle_ptr_aref, -1);
00715 rb_define_method(rb_cPointer, "[]=", rb_fiddle_ptr_aset, -1);
00716 rb_define_method(rb_cPointer, "size", rb_fiddle_ptr_size_get, 0);
00717 rb_define_method(rb_cPointer, "size=", rb_fiddle_ptr_size_set, 1);
00718
00719
00720
00721
00722
00723 rb_define_const(mFiddle, "NULL", rb_fiddle_ptr_new(0, 0, 0));
00724 }
00725