00001 #include "typelib.hh"
00002 #include <typelib/value_ops.hh>
00003 #include <ruby.h>
00004 #if !defined(RUBY_19) && !defined(RUBY_191)
00005 extern "C" {
00006 #include <st.h>
00007 }
00008 #endif
00009
00010 using namespace Typelib;
00011 using namespace std;
00012 using namespace typelib_ruby;
00013 #undef VERBOSE
00014
00015 static VALUE cMemoryZone;
00016 static st_table* MemoryTable;
00017
00018
00019
00020
00021 static int memory_table_compare(void* a, void* b)
00022 {
00023 return (a != b);
00024 }
00025
00026 #if !defined(RUBY_19) && !defined(RUBY_191)
00027 typedef long st_index_t;
00028 #endif
00029
00030 static st_index_t memory_table_hash(void* a)
00031 {
00032
00033
00034 return (st_index_t)a;
00035 }
00036
00037 #if defined(RUBY_19)
00038 static struct st_hash_type memory_table_type = {
00039 (int (*)(...))memory_table_compare,
00040 (st_index_t (*)(...))memory_table_hash
00041 };
00042 #elif defined(RUBY_191)
00043 static struct st_hash_type memory_table_type = {
00044 (int (*)(...))memory_table_compare,
00045 (int (*)(...))memory_table_hash
00046 };
00047 #else
00048 static struct st_hash_type memory_table_type = {
00049 (int (*)())memory_table_compare,
00050 (int (*)())memory_table_hash
00051 };
00052 #endif
00053
00054 struct RbMemoryLayout
00055 {
00056 int refcount;
00057 MemoryLayout layout;
00058 boost::shared_ptr<Registry> registry;
00059
00060 RbMemoryLayout()
00061 : refcount(0) {}
00062 RbMemoryLayout(MemoryLayout const& layout, boost::shared_ptr<Registry> registry)
00063 : refcount(0), layout(layout), registry(registry) {}
00064 };
00065
00066
00067
00068
00069 typedef std::map< void const*, void const* > MemoryTypes;
00070 typedef std::map< void const*, RbMemoryLayout > TypeLayouts;
00071 MemoryTypes memory_types;
00072 TypeLayouts memory_layouts;
00073
00074 static void
00075 memory_unref(void *ptr)
00076 {
00077 st_delete(MemoryTable, (st_data_t*)&ptr, 0);
00078
00079 MemoryTypes::iterator type_it = memory_types.find(ptr);
00080 if (type_it != memory_types.end())
00081 {
00082 TypeLayouts::iterator layout_it = memory_layouts.find(type_it->second);
00083 RbMemoryLayout& layout = layout_it->second;
00084 if (0 == --layout.refcount)
00085 memory_layouts.erase(layout_it);
00086 memory_types.erase(type_it);
00087 }
00088 }
00089
00090 static void
00091 memory_delete(void *ptr)
00092 {
00093 MemoryTypes::iterator it = memory_types.find(ptr);
00094 if (it != memory_types.end())
00095 {
00096 TypeLayouts::iterator layout_it = memory_layouts.find(it->second);
00097 if (layout_it != memory_layouts.end())
00098 {
00099 RbMemoryLayout& layout = layout_it->second;
00100 Typelib::destroy(
00101 static_cast<uint8_t*>(ptr),
00102 layout.layout);
00103 }
00104 }
00105
00106 free(ptr);
00107 memory_unref(ptr);
00108 }
00109
00110 static VALUE
00111 memory_aref(void *ptr)
00112 {
00113 VALUE val;
00114
00115 if (!st_lookup(MemoryTable, (st_data_t)ptr, &val)) {
00116 return Qnil;
00117 }
00118 if (val == Qundef)
00119 rb_bug("found undef in memory table");
00120
00121 return val;
00122 }
00123
00124 static void
00125 memory_aset(void *ptr, VALUE obj)
00126 {
00127 if (! NIL_P(memory_aref(ptr)))
00128 rb_raise(rb_eArgError, "there is already a wrapper for %p", ptr);
00129
00130 st_insert(MemoryTable, (st_data_t)ptr, obj);
00131 }
00132
00133 VALUE
00134 typelib_ruby::memory_allocate(size_t size)
00135 {
00136 void* ptr = malloc(size);
00137 VALUE zone = Data_Wrap_Struct(cMemoryZone, 0, &memory_delete, ptr);
00138 # ifdef VERBOSE
00139 fprintf(stderr, "%x: new allocated zone of size %i\n", ptr, size);
00140 # endif
00141 memory_aset(ptr, zone);
00142 return zone;
00143 }
00144
00145 void
00146 typelib_ruby::memory_init(VALUE ptr, VALUE type)
00147 {
00148 try {
00149 void* cptr = memory_cptr(ptr);
00150 MemoryTypes::iterator it = memory_types.find(cptr);
00151 if (it != memory_types.end())
00152 rb_raise(rb_eArgError, "memory zone already initialized");
00153
00154
00155 Type const& t(rb2cxx::object<Type>(type));
00156 TypeLayouts::iterator layout_it = memory_layouts.find(&t);
00157 if (layout_it == memory_layouts.end())
00158 {
00159 cxx2rb::RbRegistry& registry = rb2cxx::object<cxx2rb::RbRegistry>(type_get_registry(type));
00160 layout_it = memory_layouts.insert(
00161 make_pair( &t, RbMemoryLayout(layout_of(t, true), registry.registry) )
00162 ).first;
00163 }
00164 RbMemoryLayout& layout = layout_it->second;
00165 ++layout.refcount;
00166
00167 memory_types.insert( make_pair(cptr, &t) );
00168 Typelib::init(static_cast<uint8_t*>(cptr), layout.layout);
00169 } catch(std::exception const& e) {
00170 rb_raise(rb_eArgError, "internal error: %s", e.what());
00171 }
00172 }
00173
00174 VALUE
00175 typelib_ruby::memory_wrap(void* ptr)
00176 {
00177 VALUE zone = memory_aref(ptr);
00178 if (NIL_P(zone))
00179 {
00180 # ifdef VERBOSE
00181 fprintf(stderr, "%x: wrapping new memory zone\n", ptr);
00182 # endif
00183
00184 zone = Data_Wrap_Struct(cMemoryZone, 0, &memory_unref, ptr);
00185 memory_aset(ptr, zone);
00186 }
00187 else
00188 {
00189 # ifdef VERBOSE
00190 fprintf(stderr, "%x: already known memory zone\n", ptr);
00191 # endif
00192 }
00193
00194 return zone;
00195 }
00196
00197 void*
00198 typelib_ruby::memory_cptr(VALUE ptr)
00199 {
00200 void* cptr;
00201 Data_Get_Struct(ptr, void, cptr);
00202 return cptr;
00203 }
00204
00205 static VALUE
00206 memory_zone_address(VALUE self)
00207 {
00208 void* ptr = memory_cptr(self);
00209 return ULL2NUM(reinterpret_cast<uint64_t>(ptr));
00210 }
00211
00212 static VALUE
00213 memory_zone_to_ptr(VALUE self)
00214 {
00215 VALUE result = memory_allocate(sizeof(void*));
00216
00217 void* newptr = memory_cptr(result);
00218 void* ptr = memory_cptr(self);
00219
00220 *reinterpret_cast<void**>(newptr) = ptr;
00221 rb_iv_set(result, "@pointed_to_memory", self);
00222 return result;
00223 }
00224
00225 static VALUE
00226 string_to_memory_ptr(VALUE self)
00227 {
00228 rb_str_modify(self);
00229 VALUE ptr = memory_wrap(StringValuePtr(self));
00230 rb_iv_set(ptr, "@buffer_string", self);
00231 return ptr;
00232 }
00233
00234 void memory_table_mark(void* ptr)
00235 {
00236 }
00237
00238 void typelib_ruby::Typelib_init_memory()
00239 {
00240 VALUE mTypelib = rb_define_module("Typelib");
00241 MemoryTable = st_init_table(&memory_table_type);
00242 VALUE table = Data_Wrap_Struct(rb_cObject, &memory_table_mark, 0, MemoryTable);
00243 rb_iv_set(mTypelib, "@__memory_table__", table);
00244
00245 cMemoryZone = rb_define_class_under(mTypelib, "MemoryZone", rb_cObject);
00246 rb_define_method(cMemoryZone, "zone_address", RUBY_METHOD_FUNC(memory_zone_address), 0);
00247 rb_define_method(cMemoryZone, "to_ptr", RUBY_METHOD_FUNC(memory_zone_to_ptr), 0);
00248
00249 rb_define_method(rb_cString, "to_memory_ptr", RUBY_METHOD_FUNC(string_to_memory_ptr), 0);
00250 }
00251