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