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