00001 #include "typelib.hh"
00002 #include <typelib/value_ops.hh>
00003 #include <ruby.h>
00004
00005 using namespace Typelib;
00006 using namespace std;
00007 using namespace typelib_ruby;
00008
00009 static VALUE cMemoryZone;
00010 struct MemoryZone
00011 {
00012 void* ptr;
00013 MemoryZone(void* ptr)
00014 : ptr(ptr) {}
00015 };
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 static st_index_t memory_table_hash(void* a)
00027 {
00028
00029
00030 return (st_index_t)a;
00031 }
00032
00033 static struct st_hash_type memory_table_type = {
00034 (int (*)(...))memory_table_compare,
00035 (st_index_t (*)(...))memory_table_hash
00036 };
00037
00038 struct MemoryTableEntry
00039 {
00040 int refcount;
00041 VALUE object;
00042 bool owned;
00043 void* root_ptr;
00044 MemoryTableEntry(VALUE object, bool owned, void* root_ptr = 0)
00045 : refcount(1), object(object), owned(owned), root_ptr(root_ptr)
00046 {
00047 if (root_ptr && owned)
00048 rb_raise(rb_eArgError, "given both a root pointer and owned=true for object %llu", NUM2ULL(rb_obj_id(object)));
00049 }
00050 };
00051
00052 struct RbMemoryLayout
00053 {
00054 int refcount;
00055 MemoryLayout layout;
00056 boost::shared_ptr<Registry> registry;
00057
00058 RbMemoryLayout()
00059 : refcount(0) {}
00060 RbMemoryLayout(MemoryLayout const& layout, boost::shared_ptr<Registry> registry)
00061 : refcount(0), layout(layout), registry(registry) {}
00062 };
00063
00064
00065
00066
00067 typedef std::map< void const*, void const* > MemoryTypes;
00068 typedef std::map< void const*, RbMemoryLayout > TypeLayouts;
00069 MemoryTypes memory_types;
00070 TypeLayouts memory_layouts;
00071
00072 #ifdef VERBOSE
00073 static int
00074 memory_touch_i(volatile void* ptr, MemoryTableEntry* entry, st_data_t)
00075 {
00076 char value = *((char*)ptr);
00077 fprintf(stderr, "touching %p (refcount=%i)\n", ptr, entry->refcount);
00078 *((char*)ptr) = value;
00079 return ST_CONTINUE;
00080 }
00081
00082 static void
00083 memory_touch_all()
00084 {
00085 st_foreach(MemoryTable, (int(*)(ANYARGS))memory_touch_i, (st_data_t)0);
00086
00087 }
00088 #endif
00089
00090 bool
00091 typelib_ruby::memory_ref(void *ptr)
00092 {
00093 # ifdef VERBOSE
00094 fprintf(stderr, "%p: ref\n", ptr);
00095 # endif
00096
00097 MemoryTableEntry* entry = 0;
00098 if (st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry))
00099 {
00100 ++(entry->refcount);
00101 return true;
00102 }
00103 else
00104 {
00105 return false;
00106 }
00107 }
00108
00109 static void
00110 memory_zone_unref(MemoryZone* ptr)
00111 {
00112
00113 if (ptr->ptr)
00114 memory_unref(ptr->ptr);
00115 delete ptr;
00116 }
00117
00118 void
00119 typelib_ruby::memory_unref(void *ptr)
00120 {
00121 # ifdef VERBOSE
00122 fprintf(stderr, "%p: unref\n", ptr);
00123 # endif
00124
00125 MemoryTableEntry* entry = 0;
00126 if (!st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry))
00127 rb_raise(rb_eArgError, "cannot find %p in memory table", ptr);
00128 --(entry->refcount);
00129 if (entry->refcount)
00130 return;
00131
00132 if (entry->owned)
00133 memory_delete(ptr);
00134 if (entry->root_ptr)
00135 memory_unref(entry->root_ptr);
00136
00137 # ifdef VERBOSE
00138 fprintf(stderr, "%p: deregister\n", ptr);
00139 # endif
00140 delete entry;
00141 st_delete(MemoryTable, (st_data_t*)&ptr, 0);
00142
00143 MemoryTypes::iterator type_it = memory_types.find(ptr);
00144 if (type_it != memory_types.end())
00145 {
00146 TypeLayouts::iterator layout_it = memory_layouts.find(type_it->second);
00147 RbMemoryLayout& layout = layout_it->second;
00148 if (0 == --layout.refcount)
00149 memory_layouts.erase(layout_it);
00150 memory_types.erase(type_it);
00151 }
00152 }
00153
00154 void
00155 typelib_ruby::memory_delete(void *ptr)
00156 {
00157 MemoryTypes::iterator it = memory_types.find(ptr);
00158 if (it != memory_types.end())
00159 {
00160 TypeLayouts::iterator layout_it = memory_layouts.find(it->second);
00161 if (layout_it != memory_layouts.end())
00162 {
00163 RbMemoryLayout& layout = layout_it->second;
00164 Typelib::destroy(
00165 static_cast<uint8_t*>(ptr),
00166 layout.layout);
00167 }
00168 }
00169
00170 # ifdef VERBOSE
00171 fprintf(stderr, "%p: deallocated\n", ptr);
00172 # endif
00173 ruby_xfree(ptr);
00174 }
00175
00176 static VALUE
00177 memory_aref(void *ptr)
00178 {
00179 MemoryTableEntry* entry;
00180 if (!st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry)) {
00181 return Qnil;
00182 }
00183 if ((VALUE)entry == Qundef)
00184 rb_bug("found undef in memory table");
00185
00186 return entry->object;
00187 }
00188
00189 static void
00190 memory_aset(void *ptr, VALUE obj, bool owned, void* root_ptr)
00191 {
00192 if (! NIL_P(memory_aref(ptr)))
00193 rb_raise(rb_eArgError, "there is already a wrapper for %p", ptr);
00194
00195 if (ptr == root_ptr)
00196 rb_raise(rb_eArgError, "pointer and root pointer are equal");
00197
00198 if (!memory_ref(ptr))
00199 {
00200 MemoryTableEntry* entry = new MemoryTableEntry(obj, owned, root_ptr);
00201 st_insert(MemoryTable, (st_data_t)ptr, (st_data_t)entry);
00202
00203 if (root_ptr)
00204 {
00205 MemoryTableEntry* root_entry = 0;
00206 if (!st_lookup(MemoryTable, (st_data_t)root_ptr, (st_data_t*)&root_entry))
00207 rb_raise(rb_eArgError, "%p given as root pointer for %p but is not registered", root_ptr, ptr);
00208 ++(root_entry->refcount);
00209 }
00210 }
00211 }
00212
00213 VALUE
00214 typelib_ruby::memory_allocate(size_t size)
00215 {
00216 # ifdef VERBOSE
00217 memory_touch_all();
00218 # endif
00219
00220 void* ptr = ruby_xmalloc(size);
00221 # ifdef VERBOSE
00222 fprintf(stderr, "%p: new allocated zone of size %lu\n", ptr, size);
00223 # endif
00224
00225 return memory_wrap(ptr, true, 0);
00226 }
00227
00228 void
00229 typelib_ruby::memory_init(VALUE ptr, VALUE type)
00230 {
00231 try {
00232 void* cptr = memory_cptr(ptr);
00233 MemoryTypes::iterator it = memory_types.find(cptr);
00234 if (it != memory_types.end())
00235 rb_raise(rb_eArgError, "memory zone already initialized");
00236
00237
00238 Type const& t(rb2cxx::object<Type>(type));
00239 TypeLayouts::iterator layout_it = memory_layouts.find(&t);
00240 if (layout_it == memory_layouts.end())
00241 {
00242 cxx2rb::RbRegistry& registry = rb2cxx::object<cxx2rb::RbRegistry>(type_get_registry(type));
00243 layout_it = memory_layouts.insert(
00244 make_pair( &t, RbMemoryLayout(layout_of(t, true), registry.registry) )
00245 ).first;
00246 }
00247 RbMemoryLayout& layout = layout_it->second;
00248 ++layout.refcount;
00249
00250 memory_types.insert( make_pair(cptr, &t) );
00251 Typelib::init(static_cast<uint8_t*>(cptr), layout.layout);
00252 } catch(std::exception const& e) {
00253 rb_raise(rb_eArgError, "internal error: %s", e.what());
00254 }
00255 }
00256
00257 VALUE
00258 typelib_ruby::memory_wrap(void* ptr, bool take_ownership, void* root_ptr)
00259 {
00260 VALUE zone = memory_aref(ptr);
00261 if (NIL_P(zone))
00262 {
00263 # ifdef VERBOSE
00264 fprintf(stderr, "%p: wrapping new memory zone\n", ptr);
00265 # endif
00266
00267 zone = Data_Wrap_Struct(cMemoryZone, 0, &memory_zone_unref, new MemoryZone(ptr));
00268 memory_aset(ptr, zone, take_ownership, root_ptr);
00269 }
00270 else
00271 {
00272 # ifdef VERBOSE
00273 fprintf(stderr, "%p: already known memory zone\n", ptr);
00274 # endif
00275 }
00276
00277 return zone;
00278 }
00279
00280 void*
00281 typelib_ruby::memory_cptr(VALUE ptr)
00282 {
00283 MemoryZone* cptr;
00284 Data_Get_Struct(ptr, MemoryZone, cptr);
00285 return cptr->ptr;
00286 }
00287
00288 static MemoryZone*
00289 memory_zone(VALUE ptr)
00290 {
00291 MemoryZone* cptr;
00292 Data_Get_Struct(ptr, MemoryZone, cptr);
00293 return cptr;
00294 }
00295
00296
00297 static VALUE
00298 memory_zone_address(VALUE self)
00299 {
00300 void* ptr = memory_cptr(self);
00301 return ULL2NUM(reinterpret_cast<uint64_t>(ptr));
00302 }
00303
00304 static VALUE
00305 memory_zone_to_ptr(VALUE self)
00306 {
00307 # ifdef VERBOSE
00308 fprintf(stderr, "allocating void* to create a pointer-to-memory\n");
00309 # endif
00310
00311 VALUE result = memory_allocate(sizeof(void*));
00312
00313 void* newptr = memory_cptr(result);
00314 void* ptr = memory_cptr(self);
00315
00316 *reinterpret_cast<void**>(newptr) = ptr;
00317 rb_iv_set(result, "@pointed_to_memory", self);
00318 return result;
00319 }
00320
00321 static VALUE
00322 memory_zone_invalidate(VALUE self)
00323 {
00324 MemoryZone* cptr = memory_zone(self);
00325 # ifdef VERBOSE
00326 fprintf(stderr, "invalidating memory zone %p", cptr->ptr);
00327 # endif
00328 if (cptr->ptr)
00329 memory_unref(cptr->ptr);
00330 cptr->ptr = 0;
00331 return Qnil;
00332 }
00333
00334 static VALUE
00335 string_to_memory_ptr(VALUE self)
00336 {
00337 rb_str_modify(self);
00338 # ifdef VERBOSE
00339 fprintf(stderr, "wrapping string value from Ruby\n");
00340 # endif
00341 VALUE ptr = memory_wrap(StringValuePtr(self), false, 0);
00342 rb_iv_set(ptr, "@buffer_string", self);
00343 return ptr;
00344 }
00345
00346 static VALUE
00347 memory_zone_table_size(VALUE self)
00348 {
00349 return INT2NUM(MemoryTable->num_entries);
00350 }
00351
00352 void typelib_ruby::Typelib_init_memory()
00353 {
00354 VALUE mTypelib = rb_define_module("Typelib");
00355 MemoryTable = st_init_table(&memory_table_type);
00356 VALUE table = Data_Wrap_Struct(rb_cObject, 0, 0, MemoryTable);
00357 rb_iv_set(mTypelib, "@__memory_table__", table);
00358
00359 cMemoryZone = rb_define_class_under(mTypelib, "MemoryZone", rb_cObject);
00360 rb_define_method(cMemoryZone, "zone_address", RUBY_METHOD_FUNC(memory_zone_address), 0);
00361 rb_define_method(cMemoryZone, "to_ptr", RUBY_METHOD_FUNC(memory_zone_to_ptr), 0);
00362 rb_define_method(cMemoryZone, "invalidate", RUBY_METHOD_FUNC(memory_zone_invalidate), 0);
00363 rb_define_singleton_method(cMemoryZone, "table_size", RUBY_METHOD_FUNC(memory_zone_table_size), 0);
00364
00365 rb_define_method(rb_cString, "to_memory_ptr", RUBY_METHOD_FUNC(string_to_memory_ptr), 0);
00366 }
00367