ext/fiddle/pointer.c in fiddle-1.0.0 vs ext/fiddle/pointer.c in fiddle-1.0.1

- old
+ new

@@ -1,9 +1,10 @@ /* -*- C -*- * $Id$ */ +#include <stdbool.h> #include <ruby/ruby.h> #include <ruby/io.h> #include <ctype.h> #include <fiddle.h> @@ -22,10 +23,11 @@ struct ptr_data { void *ptr; long size; freefunc_t free; + bool freed; VALUE wrap[2]; }; #define RPTR_DATA(obj) ((struct ptr_data *)(DATA_PTR(obj))) @@ -55,18 +57,23 @@ rb_gc_mark(data->wrap[1]); } } static void -fiddle_ptr_free(void *ptr) +fiddle_ptr_free_ptr(void *ptr) { struct ptr_data *data = ptr; - if (data->ptr) { - if (data->free) { - (*(data->free))(data->ptr); - } + if (data->ptr && data->free && !data->freed) { + data->freed = true; + (*(data->free))(data->ptr); } +} + +static void +fiddle_ptr_free(void *ptr) +{ + fiddle_ptr_free_ptr(ptr); xfree(ptr); } static size_t fiddle_ptr_memsize(const void *ptr) @@ -87,12 +94,12 @@ VALUE val; val = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data); data->ptr = ptr; data->free = func; + data->freed = false; data->size = size; - OBJ_TAINT(val); return val; } static VALUE @@ -100,17 +107,17 @@ { return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func); } static VALUE -rb_fiddle_ptr_malloc(long size, freefunc_t func) +rb_fiddle_ptr_malloc(VALUE klass, long size, freefunc_t func) { void *ptr; ptr = ruby_xmalloc((size_t)size); memset(ptr,0,(size_t)size); - return rb_fiddle_ptr_new(ptr, size, func); + return rb_fiddle_ptr_new2(klass, ptr, size, func); } static void * rb_fiddle_ptr2cptr(VALUE val) { @@ -139,10 +146,11 @@ obj = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data); data->ptr = 0; data->size = 0; data->free = 0; + data->freed = false; return obj; } /* @@ -190,20 +198,57 @@ } return Qnil; } +static VALUE +rb_fiddle_ptr_call_free(VALUE self); + /* * call-seq: - * * Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance + * Fiddle::Pointer.malloc(size, freefunc) { |pointer| ... } => ... * + * == Examples + * + * # 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) + * ... + * * Allocate +size+ bytes of memory and associate it with an optional - * +freefunc+ that will be called when the pointer is garbage collected. + * +freefunc+. * - * +freefunc+ must be an address pointing to a function or an instance of - * Fiddle::Function + * If a block is supplied, the pointer will be yielded to the block instead of + * being returned, and the return value of the block will be returned. A + * +freefunc+ must be supplied if a block is. + * + * If a +freefunc+ is supplied it will be called once, when the pointer is + * garbage collected or when the block is left if a block is supplied or + * when the user calls +call_free+, whichever happens first. +freefunc+ must be + * an address pointing to a function or an instance of +Fiddle::Function+. */ static VALUE rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass) { VALUE size, sym, obj, wrap = 0; @@ -221,14 +266,21 @@ break; default: rb_bug("rb_fiddle_ptr_s_malloc"); } - obj = rb_fiddle_ptr_malloc(s,f); + obj = rb_fiddle_ptr_malloc(klass, s,f); if (wrap) RPTR_DATA(obj)->wrap[1] = wrap; - return obj; + 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; + } } /* * call-seq: to_i * @@ -350,10 +402,38 @@ return rb_fiddle_new_function(address, arg_types, ret_type); } /* + * call-seq: call_free => nil + * + * Call the free function for this pointer. Calling more than once will do + * nothing. Does nothing if there is no free function attached. + */ +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; +} + +/* + * call-seq: freed? => bool + * + * Returns if the free function for this pointer has been called. + */ +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; +} + +/* * call-seq: * * ptr.to_s => string * ptr.to_s(len) => string * @@ -374,15 +454,15 @@ 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_tainted_str_new2((char*)(data->ptr)); + val = rb_str_new2((char*)(data->ptr)); break; case 1: len = NUM2INT(arg1); - val = rb_tainted_str_new((char*)(data->ptr), len); + val = rb_str_new((char*)(data->ptr), len); break; default: rb_bug("rb_fiddle_ptr_to_s"); } @@ -412,15 +492,15 @@ 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_tainted_str_new((char*)(data->ptr),data->size); + val = rb_str_new((char*)(data->ptr),data->size); break; case 1: len = NUM2INT(arg1); - val = rb_tainted_str_new((char*)(data->ptr), len); + val = rb_str_new((char*)(data->ptr), len); break; default: rb_bug("rb_fiddle_ptr_to_str"); } @@ -438,11 +518,11 @@ { 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), data, data->ptr, data->size, data->free); + RB_OBJ_CLASSNAME(self), (void *)data, data->ptr, data->size, (void *)data->free); } /* * call-seq: * ptr == other => true or false @@ -540,20 +620,20 @@ 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_eFiddleError, "NULL pointer dereference"); + 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_tainted_str_new((char *)data->ptr + offset, len); + retval = rb_str_new((char *)data->ptr + offset, len); break; default: rb_bug("rb_fiddle_ptr_aref()"); } return retval; @@ -578,11 +658,11 @@ 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_eFiddleError, "NULL pointer dereference"); + 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; @@ -659,26 +739,26 @@ if (rb_obj_is_kind_of(vptr, rb_cPointer)){ ptr = vptr; wrap = 0; } else{ - rb_raise(rb_eFiddleError, "to_ptr should return a Fiddle::Pointer object"); + 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); } - OBJ_INFECT(ptr, val); if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap; return ptr; } void Init_fiddle_pointer(void) { +#undef rb_intern id_to_ptr = rb_intern("to_ptr"); /* Document-class: Fiddle::Pointer * * Fiddle::Pointer is a class to handle C pointers @@ -690,9 +770,11 @@ rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1); rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1); rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1); rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1); rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0); + rb_define_method(rb_cPointer, "call_free", rb_fiddle_ptr_call_free, 0); + rb_define_method(rb_cPointer, "freed?", rb_fiddle_ptr_freed_p, 0); rb_define_method(rb_cPointer, "to_i", rb_fiddle_ptr_to_i, 0); rb_define_method(rb_cPointer, "to_int", rb_fiddle_ptr_to_i, 0); rb_define_method(rb_cPointer, "to_value", rb_fiddle_ptr_to_value, 0); rb_define_method(rb_cPointer, "ptr", rb_fiddle_ptr_ptr, 0); rb_define_method(rb_cPointer, "+@", rb_fiddle_ptr_ptr, 0);