diff options
Diffstat (limited to 'ext/fiddle/pointer.c')
| -rw-r--r-- | ext/fiddle/pointer.c | 186 |
1 files changed, 159 insertions, 27 deletions
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c index 932bc576c5..15107e3862 100644 --- a/ext/fiddle/pointer.c +++ b/ext/fiddle/pointer.c @@ -2,11 +2,17 @@ * $Id$ */ +#include <stdbool.h> #include <ruby/ruby.h> #include <ruby/io.h> + #include <ctype.h> #include <fiddle.h> +#ifdef HAVE_RUBY_MEMORY_VIEW_H +# include <ruby/memory_view.h> +#endif + #ifdef PRIsVALUE # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj) # define RB_OBJ_STRING(obj) (obj) @@ -18,12 +24,13 @@ VALUE rb_cPointer; -typedef void (*freefunc_t)(void*); +typedef rb_fiddle_freefunc_t freefunc_t; struct ptr_data { void *ptr; long size; freefunc_t free; + bool freed; VALUE wrap[2]; }; @@ -57,14 +64,19 @@ fiddle_ptr_mark(void *ptr) } 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); } @@ -80,8 +92,40 @@ static const rb_data_type_t fiddle_ptr_data_type = { {fiddle_ptr_mark, fiddle_ptr_free, fiddle_ptr_memsize,}, }; +#ifdef HAVE_RUBY_MEMORY_VIEW_H +static struct ptr_data * +fiddle_ptr_check_memory_view(VALUE obj) +{ + struct ptr_data *data; + TypedData_Get_Struct(obj, struct ptr_data, &fiddle_ptr_data_type, data); + if (data->ptr == NULL || data->size == 0) return NULL; + return data; +} + +static bool +fiddle_ptr_memory_view_available_p(VALUE obj) +{ + return fiddle_ptr_check_memory_view(obj) != NULL; +} + +static bool +fiddle_ptr_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags) +{ + struct ptr_data *data = fiddle_ptr_check_memory_view(obj); + rb_memory_view_init_as_byte_array(view, obj, data->ptr, data->size, true); + + return true; +} + +static const rb_memory_view_entry_t fiddle_ptr_memory_view_entry = { + fiddle_ptr_get_memory_view, + NULL, + fiddle_ptr_memory_view_available_p +}; +#endif + static VALUE -rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func) +rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func, VALUE wrap0, VALUE wrap1) { struct ptr_data *data; VALUE val; @@ -89,26 +133,34 @@ rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func) 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); + data->wrap[0] = wrap0; + data->wrap[1] = wrap1; return val; } +VALUE +rb_fiddle_ptr_new_wrap(void *ptr, long size, freefunc_t func, VALUE wrap0, VALUE wrap1) +{ + return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func, wrap0, wrap1); +} + static VALUE rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func) { - return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func); + return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func, 0, 0); } 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, 0, 0); } static void * @@ -141,6 +193,7 @@ rb_fiddle_ptr_s_allocate(VALUE klass) data->ptr = 0; data->size = 0; data->free = 0; + data->freed = false; return obj; } @@ -192,16 +245,53 @@ rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self) 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) @@ -223,10 +313,17 @@ rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass) 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; + } } /* @@ -352,6 +449,34 @@ rb_fiddle_ptr_free_get(VALUE self) } /* + * 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 @@ -376,11 +501,11 @@ rb_fiddle_ptr_to_s(int argc, VALUE argv[], VALUE self) 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"); @@ -414,11 +539,11 @@ rb_fiddle_ptr_to_str(int argc, VALUE argv[], VALUE self) 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"); @@ -440,7 +565,7 @@ rb_fiddle_ptr_inspect(VALUE self) 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); } /* @@ -542,7 +667,7 @@ rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self) 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); @@ -551,7 +676,7 @@ rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self) 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()"); @@ -580,7 +705,7 @@ rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self) 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); @@ -653,6 +778,7 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) } 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){ @@ -661,7 +787,7 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) 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{ @@ -669,7 +795,6 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE 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; } @@ -677,6 +802,7 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) void Init_fiddle_pointer(void) { +#undef rb_intern id_to_ptr = rb_intern("to_ptr"); /* Document-class: Fiddle::Pointer @@ -692,6 +818,8 @@ Init_fiddle_pointer(void) 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); @@ -713,6 +841,10 @@ Init_fiddle_pointer(void) rb_define_method(rb_cPointer, "size", rb_fiddle_ptr_size_get, 0); rb_define_method(rb_cPointer, "size=", rb_fiddle_ptr_size_set, 1); +#ifdef HAVE_RUBY_MEMORY_VIEW_H + rb_memory_view_register(rb_cPointer, &fiddle_ptr_memory_view_entry); +#endif + /* Document-const: NULL * * A NULL pointer |
