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