類別 Fiddle::Pointer
Fiddle::Pointer
是用於處理 C 指標的類別
公開類別方法
取得 Ruby 物件 val
的底層指標,並以 Fiddle::Pointer
物件的形式傳回。
static VALUE rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) { VALUE ptr, wrap = val, vptr; if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){ rb_io_t *fptr; FILE *fp; GetOpenFile(val, fptr); fp = rb_io_stdio_file(fptr); ptr = rb_fiddle_ptr_new(fp, 0, NULL); } else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){ char *str = StringValuePtr(val); wrap = val; ptr = rb_fiddle_ptr_new(str, RSTRING_LEN(val), NULL); } else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){ if (rb_obj_is_kind_of(vptr, rb_cPointer)){ ptr = vptr; wrap = 0; } else{ rb_raise(rb_eFiddleDLError, "to_ptr should return a Fiddle::Pointer object"); } } else{ VALUE num = rb_Integer(val); if (num == val) wrap = 0; ptr = rb_fiddle_ptr_new(NUM2PTR(num), 0, NULL); } if (wrap) RB_OBJ_WRITE(ptr, &RPTR_DATA(ptr)->wrap[0], wrap); return ptr; }
範例¶ ↑
# Automatically freeing the pointer when the block is exited - recommended Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) do |pointer| ... end # Manually freeing but relying on the garbage collector otherwise pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) ... pointer.call_free # Relying on the garbage collector - may lead to unlimited memory allocated before freeing any, but safe pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) ... # Only manually freeing pointer = Fiddle::Pointer.malloc(size) begin ... ensure Fiddle.free pointer end # No free function and no call to free - the native memory will leak if the pointer is garbage collected pointer = Fiddle::Pointer.malloc(size) ...
配置 size
位元的記憶體,並將其與任意的 freefunc
關聯起來。
如果提供區塊,指標會傳遞給區塊,而不是傳回,而區塊的傳回值會傳回。如果提供區塊,就必須提供 freefunc
。
如果提供 freefunc
,則會呼叫一次,當指標被垃圾回收,或當離開區塊時(如果提供區塊),或當使用者呼叫 call_free
時(以先發生的為準)。freefunc
必須是指向函式的位址,或 Fiddle::Function
的實例。
static VALUE rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass) { VALUE size, sym, obj, wrap = 0; long s; freefunc_t f; switch (rb_scan_args(argc, argv, "11", &size, &sym)) { case 1: s = NUM2LONG(size); f = NULL; break; case 2: s = NUM2LONG(size); f = get_freefunc(sym, &wrap); break; default: rb_bug("rb_fiddle_ptr_s_malloc"); } obj = rb_fiddle_ptr_malloc(klass, s,f); if (wrap) RB_OBJ_WRITE(obj, &RPTR_DATA(obj)->wrap[1], wrap); if (rb_block_given_p()) { if (!f) { rb_raise(rb_eArgError, "a free function must be supplied to Fiddle::Pointer.malloc when it is called with a block"); } return rb_ensure(rb_yield, obj, rb_fiddle_ptr_call_free, obj); } else { return obj; } }
建立一個新的指標,指向 address
,並提供任意的 size
和 freefunc
。
當實例被垃圾回收時,會呼叫 freefunc
。
static VALUE rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self) { VALUE ptr, sym, size, wrap = 0, funcwrap = 0; struct ptr_data *data; void *p = NULL; freefunc_t f = NULL; long s = 0; if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) { VALUE addrnum = rb_Integer(ptr); if (addrnum != ptr) wrap = ptr; p = NUM2PTR(addrnum); } if (argc >= 2) { s = NUM2LONG(size); } if (argc >= 3) { f = get_freefunc(sym, &funcwrap); } if (p) { TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); if (data->ptr && data->free) { /* Free previous memory. Use of inappropriate initialize may cause SEGV. */ (*(data->free))(data->ptr); } RB_OBJ_WRITE(self, &data->wrap[0], wrap); RB_OBJ_WRITE(self, &data->wrap[1], funcwrap); data->ptr = p; data->size = s; data->free = f; } return Qnil; }
或讀取位址 address
長度為 len
的記憶體,並傳回包含該記憶體的字串
static VALUE rb_fiddle_ptr_read_mem(VALUE klass, VALUE address, VALUE len) { return rb_str_new((char *)NUM2PTR(address), NUM2ULONG(len)); }
取得 Ruby 物件 val
的底層指標,並以 Fiddle::Pointer
物件的形式傳回。
static VALUE rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) { VALUE ptr, wrap = val, vptr; if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){ rb_io_t *fptr; FILE *fp; GetOpenFile(val, fptr); fp = rb_io_stdio_file(fptr); ptr = rb_fiddle_ptr_new(fp, 0, NULL); } else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){ char *str = StringValuePtr(val); wrap = val; ptr = rb_fiddle_ptr_new(str, RSTRING_LEN(val), NULL); } else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){ if (rb_obj_is_kind_of(vptr, rb_cPointer)){ ptr = vptr; wrap = 0; } else{ rb_raise(rb_eFiddleDLError, "to_ptr should return a Fiddle::Pointer object"); } } else{ VALUE num = rb_Integer(val); if (num == val) wrap = 0; ptr = rb_fiddle_ptr_new(NUM2PTR(num), 0, NULL); } if (wrap) RB_OBJ_WRITE(ptr, &RPTR_DATA(ptr)->wrap[0], wrap); return ptr; }
將 str
中的位元組寫入 address
指向的位置。
static VALUE rb_fiddle_ptr_write_mem(VALUE klass, VALUE addr, VALUE str) { memcpy(NUM2PTR(addr), StringValuePtr(str), RSTRING_LEN(str)); return str; }
公開實例方法
傳回已進位 n
位元組的新指標實例。
static VALUE rb_fiddle_ptr_plus(VALUE self, VALUE other) { void *ptr; long num, size; ptr = rb_fiddle_ptr2cptr(self); size = RPTR_DATA(self)->size; num = NUM2LONG(other); return rb_fiddle_ptr_new((char *)ptr + num, size - num, 0); }
傳回已後退 n
位元組的新指標實例。
static VALUE rb_fiddle_ptr_minus(VALUE self, VALUE other) { void *ptr; long num, size; ptr = rb_fiddle_ptr2cptr(self); size = RPTR_DATA(self)->size; num = NUM2LONG(other); return rb_fiddle_ptr_new((char *)ptr - num, size + num, 0); }
如果小於,傳回 -1;如果等於,傳回 0;如果大於 other
,傳回 1。
如果 ptr
無法與 other
比較,傳回 nil。
static VALUE rb_fiddle_ptr_cmp(VALUE self, VALUE other) { void *ptr1, *ptr2; SIGNED_VALUE diff; if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qnil; ptr1 = rb_fiddle_ptr2cptr(self); ptr2 = rb_fiddle_ptr2cptr(other); diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2; if (!diff) return INT2FIX(0); return diff > 0 ? INT2NUM(1) : INT2NUM(-1); }
如果 other
包含相同的指標,傳回 true;否則傳回 false。
static VALUE rb_fiddle_ptr_eql(VALUE self, VALUE other) { void *ptr1, *ptr2; if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qfalse; ptr1 = rb_fiddle_ptr2cptr(self); ptr2 = rb_fiddle_ptr2cptr(other); return ptr1 == ptr2 ? Qtrue : Qfalse; }
傳回儲存在 index 的整數。
如果提供 start 和 length,將傳回包含從 start 到 length 的位元組的字串。
static VALUE rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self) { VALUE arg0, arg1; VALUE retval = Qnil; size_t offset, len; struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference"); switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){ case 1: offset = NUM2ULONG(arg0); retval = INT2NUM(*((char *)data->ptr + offset)); break; case 2: offset = NUM2ULONG(arg0); len = NUM2ULONG(arg1); retval = rb_str_new((char *)data->ptr + offset, len); break; default: rb_bug("rb_fiddle_ptr_aref()"); } return retval; }
Set
將 index
的值設為 int
。
或者,將 start
到 length
的記憶體設為 string
的內容、dl_cptr
的記憶體或 addr
記憶體位址所指的記憶體。
static VALUE rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self) { VALUE arg0, arg1, arg2; VALUE retval = Qnil; size_t offset, len; void *mem; struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference"); switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){ case 2: offset = NUM2ULONG(arg0); ((char*)data->ptr)[offset] = NUM2UINT(arg1); retval = arg1; break; case 3: offset = NUM2ULONG(arg0); len = NUM2ULONG(arg1); if (RB_TYPE_P(arg2, T_STRING)) { mem = StringValuePtr(arg2); } else if( rb_obj_is_kind_of(arg2, rb_cPointer) ){ mem = rb_fiddle_ptr2cptr(arg2); } else{ mem = NUM2PTR(arg2); } memcpy((char *)data->ptr + offset, mem, len); retval = arg2; break; default: rb_bug("rb_fiddle_ptr_aset()"); } return retval; }
呼叫此指標的免費函式。呼叫多次不會有任何作用。如果沒有附加免費函式,則不會執行任何動作。
static VALUE rb_fiddle_ptr_call_free(VALUE self) { struct ptr_data *pdata; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); fiddle_ptr_free_ptr(pdata); return Qnil; }
取得此指標的免費函式。
傳回 Fiddle::Function
的新實例。
static VALUE rb_fiddle_ptr_free_get(VALUE self) { struct ptr_data *pdata; VALUE address; VALUE arg_types; VALUE ret_type; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); if (!pdata->free) return Qnil; address = PTR2NUM(pdata->free); ret_type = INT2NUM(TYPE_VOID); arg_types = rb_ary_new(); rb_ary_push(arg_types, INT2NUM(TYPE_VOIDP)); return rb_fiddle_new_function(address, arg_types, ret_type); }
Set
這個指標的自由函式到指定的 Fiddle::Function
中。
static VALUE rb_fiddle_ptr_free_set(VALUE self, VALUE val) { struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); data->free = get_freefunc(val, &data->wrap[1]); return Qnil; }
傳回是否已呼叫此指標的自由函式。
static VALUE rb_fiddle_ptr_freed_p(VALUE self) { struct ptr_data *pdata; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); return pdata->freed ? Qtrue : Qfalse; }
傳回一個字串,其中格式化為指標內部狀態的易讀表示形式。
static VALUE rb_fiddle_ptr_inspect(VALUE self) { struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>", RB_OBJ_CLASSNAME(self), (void *)data, data->ptr, data->size, (void *)data->free); }
如果這是個空指標,傳回 true
。
static VALUE rb_fiddle_ptr_null_p(VALUE self) { struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); return data->ptr ? Qfalse : Qtrue; }
傳回新的 Fiddle::Pointer
實例,這是此指標的取消參考指標。
類似於 C 中的星號運算子。
static VALUE rb_fiddle_ptr_ptr(VALUE self) { struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); return rb_fiddle_ptr_new(*((void**)(data->ptr)),0,0); }
傳回新的 Fiddle::Pointer
實例,這是此指標的參考指標。
類似於 C 中的縮寫符號運算子。
static VALUE rb_fiddle_ptr_ref(VALUE self) { struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); return rb_fiddle_ptr_new(&(data->ptr),0,0); }
取得此指標的大小。
static VALUE rb_fiddle_ptr_size_get(VALUE self) { return LONG2NUM(RPTR_DATA(self)->size); }
Set
此指標的大小為 size
static VALUE rb_fiddle_ptr_size_set(VALUE self, VALUE size) { RPTR_DATA(self)->size = NUM2LONG(size); return size; }
傳回此指標的整數記憶體位置。
static VALUE rb_fiddle_ptr_to_i(VALUE self) { struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); return PTR2NUM(data->ptr); }
ptr.to_s => string ptr.to_s(len) => string
傳回指標內容作為字串。
如果未帶任何引數呼叫此方法,此方法將傳回內容,直到第一個 NULL 位元組為止。
如果帶有 len
呼叫,將傳回一個長度為 len
位元組的字串。
請參閱 to_str
static VALUE rb_fiddle_ptr_to_s(int argc, VALUE argv[], VALUE self) { struct ptr_data *data; VALUE arg1, val; int len; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); switch (rb_scan_args(argc, argv, "01", &arg1)) { case 0: val = rb_str_new2((char*)(data->ptr)); break; case 1: len = NUM2INT(arg1); val = rb_str_new((char*)(data->ptr), len); break; default: rb_bug("rb_fiddle_ptr_to_s"); } return val; }
ptr.to_str => string ptr.to_str(len) => string
傳回指標內容作為字串。
如果未帶任何引數呼叫此方法,此方法將傳回長度為此指標 size
的內容。
如果帶有 len
呼叫,將傳回一個長度為 len
位元組的字串。
請參閱 to_s
static VALUE rb_fiddle_ptr_to_str(int argc, VALUE argv[], VALUE self) { struct ptr_data *data; VALUE arg1, val; int len; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); switch (rb_scan_args(argc, argv, "01", &arg1)) { case 0: val = rb_str_new((char*)(data->ptr),data->size); break; case 1: len = NUM2INT(arg1); val = rb_str_new((char*)(data->ptr), len); break; default: rb_bug("rb_fiddle_ptr_to_str"); } return val; }
將此指標轉換為 Ruby 物件。
static VALUE rb_fiddle_ptr_to_value(VALUE self) { struct ptr_data *data; TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); return (VALUE)(data->ptr); }