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