Ruby 2.0.0p247(2013-06-27revision41674)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

cptr.c
Go to the documentation of this file.
1 /* -*- C -*-
2 * $Id: cptr.c 34400 2012-01-30 21:47:35Z nobu $
3 */
4
5 #include <ruby/ruby.h>
6 #include <ruby/io.h>
7 #include <ctype.h>
8 #include "dl.h"
9
10 VALUE rb_cDLCPtr;
11
12 static inline freefunc_t
13 get_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
29 static ID id_to_ptr;
30
31 static void
32 dlptr_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
43 static void
44 dlptr_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
54 static size_t
55 dlptr_memsize(const void *ptr)
56 {
57 const struct ptr_data *data = ptr;
58 return data ? sizeof(*data) + data->size : 0;
59 }
60
61 static const rb_data_type_t dlptr_data_type = {
62 "dl/ptr",
63 {dlptr_mark, dlptr_free, dlptr_memsize,},
64 };
65
66 VALUE
67 rb_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
82 VALUE
83 rb_dlptr_new(void *ptr, long size, freefunc_t func)
84 {
85 return rb_dlptr_new2(rb_cDLCPtr, ptr, size, func);
86 }
87
88 VALUE
89 rb_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
99 void *
100 rb_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
119 static VALUE
120 rb_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 */
143 static VALUE
144 rb_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 */
190 static VALUE
191 rb_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 */
221 static VALUE
222 rb_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 */
235 static VALUE
236 rb_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 */
249 VALUE
250 rb_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 */
264 VALUE
265 rb_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 */
278 VALUE
279 rb_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 */
292 static VALUE
293 rb_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 */
308 static VALUE
309 rb_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 */
328 static VALUE
329 rb_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 */
361 static VALUE
362 rb_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 */
390 static VALUE
391 rb_dlptr_inspect(VALUE self)
392 {
393 struct ptr_data *data;
394 char str[1024];
395
396 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
397 snprintf(str, 1023, "#<%s:%p ptr=%p size=%ld free=%p>",
398 rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free);
399 return rb_str_new2(str);
400 }
401
402 /*
403 * call-seq:
404 * ptr == other => true or false
405 * ptr.eql?(other) => true or false
406 *
407 * Returns true if +other+ wraps the same pointer, otherwise returns
408 * false.
409 */
410 VALUE
411 rb_dlptr_eql(VALUE self, VALUE other)
412 {
413 void *ptr1, *ptr2;
414
415 if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qfalse;
416
417 ptr1 = rb_dlptr2cptr(self);
418 ptr2 = rb_dlptr2cptr(other);
419
420 return ptr1 == ptr2 ? Qtrue : Qfalse;
421 }
422
423 /*
424 * call-seq:
425 * ptr <=> other => -1, 0, 1, or nil
426 *
427 * Returns -1 if less than, 0 if equal to, 1 if greater than +other+. Returns
428 * nil if +ptr+ cannot be compared to +other+.
429 */
430 static VALUE
431 rb_dlptr_cmp(VALUE self, VALUE other)
432 {
433 void *ptr1, *ptr2;
434 SIGNED_VALUE diff;
435
436 if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qnil;
437
438 ptr1 = rb_dlptr2cptr(self);
439 ptr2 = rb_dlptr2cptr(other);
440 diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2;
441 if (!diff) return INT2FIX(0);
442 return diff > 0 ? INT2NUM(1) : INT2NUM(-1);
443 }
444
445 /*
446 * call-seq:
447 * ptr + n => new cptr
448 *
449 * Returns a new DL::CPtr that has been advanced +n+ bytes.
450 */
451 static VALUE
452 rb_dlptr_plus(VALUE self, VALUE other)
453 {
454 void *ptr;
455 long num, size;
456
457 ptr = rb_dlptr2cptr(self);
458 size = RPTR_DATA(self)->size;
459 num = NUM2LONG(other);
460 return rb_dlptr_new((char *)ptr + num, size - num, 0);
461 }
462
463 /*
464 * call-seq:
465 * ptr - n => new cptr
466 *
467 * Returns a new DL::CPtr that has been moved back +n+ bytes.
468 */
469 static VALUE
470 rb_dlptr_minus(VALUE self, VALUE other)
471 {
472 void *ptr;
473 long num, size;
474
475 ptr = rb_dlptr2cptr(self);
476 size = RPTR_DATA(self)->size;
477 num = NUM2LONG(other);
478 return rb_dlptr_new((char *)ptr - num, size + num, 0);
479 }
480
481 /*
482 * call-seq:
483 * ptr[index] -> an_integer
484 * ptr[start, length] -> a_string
485 *
486 * Returns integer stored at _index_. If _start_ and _length_ are given,
487 * a string containing the bytes from _start_ of length _length_ will be
488 * returned.
489 */
490 VALUE
491 rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
492 {
493 VALUE arg0, arg1;
494 VALUE retval = Qnil;
495 size_t offset, len;
496 struct ptr_data *data;
497
498 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
499 if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference");
500 switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
501 case 1:
502 offset = NUM2ULONG(arg0);
503 retval = INT2NUM(*((char *)data->ptr + offset));
504 break;
505 case 2:
506 offset = NUM2ULONG(arg0);
507 len = NUM2ULONG(arg1);
508 retval = rb_tainted_str_new((char *)data->ptr + offset, len);
509 break;
510 default:
511 rb_bug("rb_dlptr_aref()");
512 }
513 return retval;
514 }
515
516 /*
517 * call-seq:
518 * ptr[index] = int -> int
519 * ptr[start, length] = string or cptr or addr -> string or dl_cptr or addr
520 *
521 * Set the value at +index+ to +int+. Or, set the memory at +start+ until
522 * +length+ with the contents of +string+, the memory from +dl_cptr+, or the
523 * memory pointed at by the memory address +addr+.
524 */
525 VALUE
526 rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
527 {
528 VALUE arg0, arg1, arg2;
529 VALUE retval = Qnil;
530 size_t offset, len;
531 void *mem;
532 struct ptr_data *data;
533
534 TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
535 if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference");
536 switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
537 case 2:
538 offset = NUM2ULONG(arg0);
539 ((char*)data->ptr)[offset] = NUM2UINT(arg1);
540 retval = arg1;
541 break;
542 case 3:
543 offset = NUM2ULONG(arg0);
544 len = NUM2ULONG(arg1);
545 if (RB_TYPE_P(arg2, T_STRING)) {
546 mem = StringValuePtr(arg2);
547 }
548 else if( rb_obj_is_kind_of(arg2, rb_cDLCPtr) ){
549 mem = rb_dlptr2cptr(arg2);
550 }
551 else{
552 mem = NUM2PTR(arg2);
553 }
554 memcpy((char *)data->ptr + offset, mem, len);
555 retval = arg2;
556 break;
557 default:
558 rb_bug("rb_dlptr_aset()");
559 }
560 return retval;
561 }
562
563 /*
564 * call-seq: size=(size)
565 *
566 * Set the size of this pointer to +size+
567 */
568 static VALUE
569 rb_dlptr_size_set(VALUE self, VALUE size)
570 {
571 RPTR_DATA(self)->size = NUM2LONG(size);
572 return size;
573 }
574
575 /*
576 * call-seq: size
577 *
578 * Get the size of this pointer.
579 */
580 static VALUE
581 rb_dlptr_size_get(VALUE self)
582 {
583 return LONG2NUM(RPTR_DATA(self)->size);
584 }
585
586 /*
587 * call-seq:
588 * DL::CPtr.to_ptr(val) => cptr
589 * DL::CPtr[val] => cptr
590 *
591 * Get the underlying pointer for ruby object +val+ and return it as a
592 * DL::CPtr object.
593 */
594 static VALUE
595 rb_dlptr_s_to_ptr(VALUE self, VALUE val)
596 {
597 VALUE ptr, wrap = val, vptr;
598
599 if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){
600 rb_io_t *fptr;
601 FILE *fp;
602 GetOpenFile(val, fptr);
603 fp = rb_io_stdio_file(fptr);
604 ptr = rb_dlptr_new(fp, 0, NULL);
605 }
606 else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){
607 char *str = StringValuePtr(val);
608 ptr = rb_dlptr_new(str, RSTRING_LEN(val), NULL);
609 }
610 else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){
611 if (rb_obj_is_kind_of(vptr, rb_cDLCPtr)){
612 ptr = vptr;
613 wrap = 0;
614 }
615 else{
616 rb_raise(rb_eDLError, "to_ptr should return a CPtr object");
617 }
618 }
619 else{
620 VALUE num = rb_Integer(val);
621 if (num == val) wrap = 0;
622 ptr = rb_dlptr_new(NUM2PTR(num), 0, NULL);
623 }
624 OBJ_INFECT(ptr, val);
625 if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap;
626 return ptr;
627 }
628
629 void
630 Init_dlptr(void)
631 {
632 id_to_ptr = rb_intern("to_ptr");
633
634 /* Document-class: DL::CPtr
635 *
636 * CPtr is a class to handle C pointers
637 *
638 */
639 rb_cDLCPtr = rb_define_class_under(rb_mDL, "CPtr", rb_cObject);
640 rb_define_alloc_func(rb_cDLCPtr, rb_dlptr_s_allocate);
641 rb_define_singleton_method(rb_cDLCPtr, "malloc", rb_dlptr_s_malloc, -1);
642 rb_define_singleton_method(rb_cDLCPtr, "to_ptr", rb_dlptr_s_to_ptr, 1);
643 rb_define_singleton_method(rb_cDLCPtr, "[]", rb_dlptr_s_to_ptr, 1);
644 rb_define_method(rb_cDLCPtr, "initialize", rb_dlptr_initialize, -1);
645 rb_define_method(rb_cDLCPtr, "free=", rb_dlptr_free_set, 1);
646 rb_define_method(rb_cDLCPtr, "free", rb_dlptr_free_get, 0);
647 rb_define_method(rb_cDLCPtr, "to_i", rb_dlptr_to_i, 0);
648 rb_define_method(rb_cDLCPtr, "to_int", rb_dlptr_to_i, 0);
649 rb_define_method(rb_cDLCPtr, "to_value", rb_dlptr_to_value, 0);
650 rb_define_method(rb_cDLCPtr, "ptr", rb_dlptr_ptr, 0);
651 rb_define_method(rb_cDLCPtr, "+@", rb_dlptr_ptr, 0);
652 rb_define_method(rb_cDLCPtr, "ref", rb_dlptr_ref, 0);
653 rb_define_method(rb_cDLCPtr, "-@", rb_dlptr_ref, 0);
654 rb_define_method(rb_cDLCPtr, "null?", rb_dlptr_null_p, 0);
655 rb_define_method(rb_cDLCPtr, "to_s", rb_dlptr_to_s, -1);
656 rb_define_method(rb_cDLCPtr, "to_str", rb_dlptr_to_str, -1);
657 rb_define_method(rb_cDLCPtr, "inspect", rb_dlptr_inspect, 0);
658 rb_define_method(rb_cDLCPtr, "<=>", rb_dlptr_cmp, 1);
659 rb_define_method(rb_cDLCPtr, "==", rb_dlptr_eql, 1);
660 rb_define_method(rb_cDLCPtr, "eql?", rb_dlptr_eql, 1);
661 rb_define_method(rb_cDLCPtr, "+", rb_dlptr_plus, 1);
662 rb_define_method(rb_cDLCPtr, "-", rb_dlptr_minus, 1);
663 rb_define_method(rb_cDLCPtr, "[]", rb_dlptr_aref, -1);
664 rb_define_method(rb_cDLCPtr, "[]=", rb_dlptr_aset, -1);
665 rb_define_method(rb_cDLCPtr, "size", rb_dlptr_size_get, 0);
666 rb_define_method(rb_cDLCPtr, "size=", rb_dlptr_size_set, 1);
667
668 /* Document-const: NULL
669 *
670 * A NULL pointer
671 */
672 rb_define_const(rb_mDL, "NULL", rb_dlptr_new(0, 0, 0));
673 }
674